Given a binary format with a header that include the number of records and records of format:
{ type : Int8, timestamp : UInt32, user_id : UInt64 }
0000 0004 0153 0927 d139 6747 c045 d991
2100 53d1 6287 4fd2 69fd 8e5f 0475 0153
f323 a72b 4984 a40b 8d54 db00 53a0 78d4
1db8 b1a6 4129 1651
I'm coming from Ruby and I have the following solution which works, but I think there might be a more elegant or 'crystal' way to read the bytes when it's structured data?
class User
USER_TYPES = {
0 => :admin,
1 => :user,
}
property user_type : Symbol
property timestamp : UInt32
property user_id : UInt64
def initialize(user_type : Int8, #timestamp : UInt32, #user_id : UInt64)
#user_type = USER_TYPES[user_type]
end
end
class Parser
property users : Array(User)
def initialize
#users = [] of User
end
def parse(file_path : String)
File.open(file_path) do |file|
offset = 0
count : UInt32 = seek_and_unpack(file, offset, UInt32)
offset += 4
(0..count).each do |i|
user_type = seek_and_unpack(file, offset, Int8)
timestamp = seek_and_unpack(file, offset + 1, UInt32)
user_id = seek_and_unpack(file, offset + 5, UInt64)
user = User.new(user_type, timestamp, user_id)
#users << user
offset += 13
end
#users
end
end
private def seek_and_unpack(file : File, offset : Int32, read_type)
file.seek(offset)
file.read_bytes(read_type, IO::ByteFormat::BigEndian)
end
end
puts Parser.new.parse("my_file.dat")
# [#<User:0x102805fe0 #user_type=:user, #timestamp=1393108945, #user_id=4136353673894269217>,
# #<User:0x102805fc0 #user_type=:admin, #timestamp=1406231175, #user_id=5751776211841778805>,
# #<User:0x102805fa0 #user_type=:user, #timestamp=1408443303, #user_id=3119170057034093787>,
# #<User:0x102805f80 #user_type=:admin, #timestamp=1403025620, #user_id=2141656950430570065>]
You can get rid of the seeks, since read_bytes already seeks the IO and wrap the unpack operation in a macro to make it more readable:
class Parser
property users
#users = [] of User
def parse(path)
File.open(path) do |file|
count = unpack(UInt32)
count.times do
#users << User.new(
user_type: unpack(Int8),
timestamp: unpack(UInt32),
user_id: unpack(UInt64)
)
end
#users
end
end
macro unpack(type)
file.read_bytes({{type}}, IO::ByteFormat::BigEndian)
end
end
you can also use https://github.com/spider-gazelle/bindata which is a little more declarative and also handles endianness for you.
require "bindata"
enum UserType
Admin = 0
User
end
class User < BinData
endian big
enum_field UInt8, type : UserType = UserType::User
uint32 :timestamp
uint64 :user_id
end
users = [] of User
File.open(path) do |file|
count = file.read_bytes(UInt32, :big_endian)
count.times do
users << file.read_bytes(User)
end
end
users
There multiple possibilities to make your code as you described it more 'crystal'
You could use IO#read instead of IO#seek and IO#read_bytes:
def parse(file_path : String)
File.open(file_path, "rb") do |file|
count : UInt32 = file.read(UInt32).first
(0..count).each do |i|
user_type = file.read(Int8).first
timestamp = file.read(UInt32).first
user_id = file.read(UInt64).first
user = User.new(user_type, timestamp, user_id)
#users << user
end
#users
end
end
Adding to this you could use IO#each_slice to iterate over the records of the file instead of incrementing an offset
def parse(file_path : String)
File.open(file_path, "rb") do |file|
count : UInt32 = file.read(UInt32).first
file.each_slice(13) do |slice|
user_type = slice[0]
timestamp = slice[1..4].pack("C*").unpack(UInt32).first
user_id = slice[5..12].pack("C*").unpack(UInt64).first
user = User.new(user_type, timestamp, user_id)
#users << user
end
#users
end
end
You could also use IO#each_struct so you can directly unpack the records into a struct:
struct Record
type : Int8
timestamp : UInt32
user_id : UInt64
end
def parse(file_path : String)
File.open(file_path, "rb") do |file|
count : UInt32 = file.read(UInt32).first
file.each_struct(Record) do |record|
user_type = USER_TYPES[record.type]
timestamp = record.timestamp
user_id = record.user_id
user = User.new(user_type, timestamp, user_id)
#users << user
end
#users
end
end
Related
I have a Item model with a numeric number field. This number field defaults to null.
# models.py
class Item(models.Model):
number = models.IntegerField(default=None, blank=True, null=True)
I want to set-up filters that can return a queryset of Items where number is in range - which is straightforward enough:
# filters.py
class ItemFilter(django_filters.FilterSet):
min_num = django_filters.NumberFilter(method="min_num_filter")
max_num = django_filters.NumberFilter(method="max_num_filter")
class Meta:
model = Item
fields = ("min_num", "max_num", "incl_null")
def min_num_filter(self, queryset, name, value):
return queryset.filter(number__gte=value)
def max_num_filter(self, queryset, name, value):
return queryset.filter(number__lte=value)
But what if I want to have an additional Boolean filter that can include Items that has null for number along with whatever Items matches the min_num and max_num range?
So for example, a URL query in the form of ?min_num=1&max_num=10&incl_null=True should return all Items where number is between 1 and 10 OR number is equal to None.
The following code does not work:
class ItemFilter(django_filters.FilterSet):
...
incl_null = django_filters.BooleanFilter(method="incl_null_filter")
class Meta:
model = Item
fields = ("min_num", "max_num", "incl_null")
// doesn't work
class incl_null_filter(self, queryset, name, value):
if value is True:
return queryset | Item.objects.filter(number=None)
if value is False:
return queryset
Edit: I've tried the methods in the "Filtering by empty values" documentation but I think that's for null values exclusively - where I'm looking for a range match OR a null value.
Try this query:
from django.db.models import Q
min_ = 0
max_ = 10
Item.objects.filter(Q(number__gte=min_, number__lte=max_) | Q(number__isnull=True))
Well, the only solution I can think of is to pass the min range, max range, and is_null boolean into a single char field then convert it into the 3 individual filters for actioning.
So the query URL will look like ?master_num=1-10-1 for range 1 - 10 incl. None and ?master_num=1-10-0 for range 1 - 10 excl. None.
class ItemFilter(django_filters.FilterSet):
master_num = django_filters.CharFilter(method="master_num_filter")
class Meta:
model = Item
fields = ("master_num")
def master_num_filter(self, queryset, name, value):
# array = [min, max, 1 or 0 for True and False]
array = value.split("-")
min = Q(year_published__gte=int(array[0]))
max = Q(year_published__lte=int(array[1]))
if array[2] == "1":
incl_null = Q(year_published=None)
return queryset.filter((min & max) | incl_null)
else:
return queryset.filter(min & max)
Would like to know if there's a better way to do this.
How to make the query take correct date from start date and end end of the day for FilterSet.
Now if you type start_filter_date = 2018.05.23 and end_filter_date = 2018.05.25. Then start_filter_date=2018.05.23T00:00 and end_filter_date=2018.05.25T00:00. Time is taken 00:00, but need to be 23:59?
My class is following:
class TaskFilterSet(django_filters.rest_framework.FilterSet):
id = django_filters.NumberFilter(name="pk")
start_filter_date = django_filters.DateFilter(name="date_added", lookup_expr="gte")
end_filter_date = django_filters.DateFilter(name="date_added", lookup_expr="lte")
i think the best way is use gte with lt but send end date date_added + timedelta(1), i don't use the django filters but based on the tips.html#solution-1-magic-values you can try:
class EndFilter(django_filters.DateFilter):
def filter(self, qs, value):
if value:
value = value + timdelta(1)
return super(EndFilter, self).filter(qs, value)
class TaskFilterSet(django_filters.rest_framework.FilterSet):
id = django_filters.NumberFilter(name="pk")
start_filter_date = django_filters.DateFilter(name="date_added", lookup_expr="gte")
end_filter_date = EndFilter(name="date_added", lookup_expr="lt")
In Django admin, if I want to display a list of Iron and their respective formatted weights, I would have to do this.
class IronAdmin(admin.ModelAdmin):
model = Iron
fields = ('weight_formatted',)
def weight_formatted(self, object):
return '{0:.2f} Kg'.format(object.weight)
weight_formatted.short_description = 'Weight'
I.e: 500.00 Kg
The problem with this however is that I would have to write a method for every field that I want to format, making it redundant when I have 10 or more objects to format.
Is there a method that I could override to "catch" these values and specify formatting before they get rendered onto the html? I.e. instead of having to write a method for each Admin class, I could just write the following and have it be formatted.
class IronAdmin(admin.ModelAdmin):
model = Iron
fields = ('weight__kg',)
def overriden_method(field):
if field.name.contains('__kg'):
field.value = '{0:.2f} Kg'.format(field.value)
I.e: 500.00 Kg
After hours scouring the source , I finally figured it out! I realize this isn't the most efficient code and it's probably more trouble than it's worth in most use cases but it's enough for me. In case anyone else needs a quick and dirty way to do it:
In order to automate it, I had to override django.contrib.admin.templatetags.admin_list.result_list with the following:
def result_list_larz(cl):
"""
Displays the headers and data list together
"""
resultz = list(results(cl)) # Where we override
""" Overriding starts here """
""" Have to scrub the __kg's as result_header(cl) will error out """
for k in cl.list_display:
cl.list_display[cl.list_display.index(k)] = k.replace('__kg','').replace('__c','')
headers = list(result_headers(cl))
num_sorted_fields = 0
for h in headers:
if h['sortable'] and h['sorted']:
num_sorted_fields += 1
return {'cl': cl,
'result_hidden_fields': list(result_hidden_fields(cl)),
'result_headers': headers,
'num_sorted_fields': num_sorted_fields,
'results': resultz}
Then overriding results(cl)'s call to items_for_result() wherein we then override its call to lookup_field() as follows:
def lookup_field(name, obj, model_admin=None):
opts = obj._meta
try:
f = _get_non_gfk_field(opts, name)
except (FieldDoesNotExist, FieldIsAForeignKeyColumnName):
# For non-field values, the value is either a method, property or
# returned via a callable.
if callable(name):
attr = name
value = attr(obj)
elif (model_admin is not None and
hasattr(model_admin, name) and
not name == '__str__' and
not name == '__unicode__'):
attr = getattr(model_admin, name)
value = attr(obj)
""" Formatting code here """
elif '__kg' in name or '__c' in name: # THE INSERT FOR FORMATTING!
actual_name = name.replace('__kg','').replace('__c', '')
value = getattr(obj, actual_name)
value = '{0:,.2f}'.format(value)
prefix = ''
postfix = ''
if '__kg' in name:
postfix = ' Kg'
elif '__c' in name:
prefix = 'P'
value = '{}{}{}'.format(prefix, value, postfix)
attr = value
else:
attr = getattr(obj, name)
if callable(attr):
value = attr()
else:
value = attr
f = None
""" Overriding code END """
else:
attr = None
value = getattr(obj, name)
return f, attr, value
I did the code for insert records from so_parts table to so_bo table using Query...How can I use ORM method to do this kind of job. Is there any other way(best)to do that? Here is my code`
`
#api.multi
def save_rapair_parts(self, vals):
#get todays date and convert it to string
created_date = datetime.datetime.today().strftime("%m/%d/%Y")
str_date = str(created_date)
so_p_id = self.so_p_id.id
bo_status = self.bo_status
so_part_t = self.so_part_t
so_part_sno = self.so_part_sno
product = self.so_part_product
product_str = 'Repair '+str(product)
part_id = self.id
bench_order_table.search(['id','bo_sno','created_date','bo_number','rep_description','bo_status'])
#insert details intoso bench orders
`
if so_part_t=='r_b':
try:
sequence = self.env['ir.sequence'].next_by_code('so.benchorder') or '/'
str_sequence = str(sequence)
query = """SELECT so_work_authorization FROM my_depots_so WHERE id=%d """ % (so_p_id)
self.env.cr.execute(query)
result = self.env.cr.fetchall()
result_number = json.dumps(result, ensure_ascii=False)
strip_number = result_number.strip('\' \" [] ')
work_auth_no = str(strip_number)
work_auth_no += "-"
work_auth_no += str_sequence
insert ="""INSERT INTO my_depots_so_bo(id,so_bo_id,bo_sno,created_date,bo_number,rep_description,bo_status) values %s """
parameters = (part_id,so_p_id,so_part_sno,str_date,work_auth_no,product_str,bo_status)
self.env.cr.execute(insert,(parameters,))
my_depots_bo(id,bo_sno,created_date,bo_number,rep_description,bo_status) values %s """
# self.env.cr.execute(insert_query, (parameters,))
except Exception:
print "Error in inserting values"`
yes there is a better way because when you use ORM
method you also checks access right for user to:
for your select query:
rec = self.env['my.depots.so'].search_read(['id', '=', so_p_id], ['so_work_authorization'])
if rec:
rec = rec[0] # search_read return a list of dictionary
so_work_authorization = rec['so_work_authorization']
# and do what ever you want with the result
# to create
# call create method witch accept a dictionary
# field_name : value
new_rec = self.env['my.depots.so.bo'].create({
'so_bo_id': so_p_id, # many2one must be an integer value
'bo_sno': bo_nso_value,
'bo_number': value_of_number,
# ....
# ....
# add al field
}) # create return the new created record as model object
for inserting use: self.env['model.name'].create(vals)
for updating use : self.env['model.name'].write(vals)
using ORM method makes sure that user don't pass the security access rigths
Hope you get the idea
I am trying to use web2py to build an app. I have a simple print function that a user submits a key word . The string or int key word is directed to an sqlite db to retrieve a row and output the data. I need to know
1. how to use the print on html.
2. How to split the string...so far i did the list:string
Here is my code:
def first():
form = SQLFORM.factory(Field('visitor_name',
label = 'Please Type Your keyword here!',
requires= [IS_NOT_EMPTY(), IS_LOWER(),'list:string']))
form.element('input[type=submit]')['_onclick'] = "return \
confirm('Are you sure you want to submit:');"
if form.process().accepted:
session.visitor_name = form.vars.visitor_name
redirect(URL('main'))
return dict(form=form)
def main():
while True:
name = request.vars.visitor_name or redirect(URL('first'))
name2 = name[:]
for item in name2:break
name3 = ' '.join(name2)
import sqlite3
id = 0
location = ""
conn = sqlite3.connect("keywords.db")
c = conn.cursor()
c.execute('select * from kmedicals')
records = c.fetchall()
for record in records:
id = record[0]
location = record[15]
if id == name3:
print name3.capitalize(),':' '\n',location
break
sys.exit()
return dict(name=name)
my view...default/main.html:
{{extend 'layout.html'}}
{{=name}}