Adding multiple variables in an object - need further assistance - python

First thing first - upon suggestions, please do your best to be understandable for newbies as if it is too complex, it may not be much useful as I need to furtherly continue after the current ask. Thank you in advance for that :)
I'm trying to define an object with multiple variables that I may use.
So far I was able to create the basic class for myself (with just ID of the object), but I am now struggling to add the rest of the variables needed for the object.
The data that I have to store with the multiple instance of the object is as follows:
id of the user - this is the value thru which I need to be searching thru the objects as I will have multiple entries of the below example data for different time intervals that I need to count. It does not need to be changed within the objects variables.
Name - The name of the person for whom I will be counting the hours spent. It is static (does not need to be changed within the objects variables).
Started timestamp and Ended timestamp - The time within which the person has executed things. As I will have multiple instances of data coming towards the object, I need to check for overlapping of shifts and if so, such hours to be avoided, but if extra hours beside the overlapped - to be added. E.g. if overlapping is not a perfect match, then the additional time spent to be added to the "total spent hours". The data received for both timestamps are in format that I convert into datatime with "datetime.strptime(start, '%Y-%m-%dT%H:%M:%S+02:00')
Schedule ID - it is the ID of the entry for the started and ended timestamps. It may be saved as an array as it will not be used except for reporting purposes - e.g. the person has processed things during it's first shift (start_timestamp thru end_timestamp).
Array of contacts that I need to separate to two different values - one for e-mail, other for phone number (including country code). The array returns as [email, country_code, phone_number]
Quote of example data that I have:
PersonID: ID1234
Name: Anton Todorov
Started at: 2022-12-26T00:00:00+02:00
Ended at: 2022-12-26T02:00:00+02:00
Schedule ID: SCHEDID1
Contacts: ['a.todorov#e-mail.email', 359, '000000000']
---===Separator===---
PersonID: ID5678
Name: Morgan Freeman
Started at: 2022-12-26T02:00:00+02:00
Ended at: 2022-12-26T14:00:00+02:00
Schedule ID: SCHEDID2
Contacts: ['slogan#draftkings.com', 1, '0000000000']
---===Separator===---
PersonID: ID1234
Name: Anton Todorov
Started at: 2022-12-26T14:00:00+02:00
Ended at: 2022-12-27T02:00:00+02:00
Schedule ID: SCHEDID3
Contacts: ['a.todorov#e-mail.email', 359, '000000000']
So with that on, I have to calculate the total hours that each person has spend from within these sections of data that I have.
The object that I have so far is as follows:
class DataItem(object):
def __init__(self, person_id):
self._person_id = person_id
self._updatable_id = ""
#property
def person_id(self):
return self._person_id
#property
def updatable_id(self):
return self._updatable_id
#updatable_id.setter
def updatable_id(self, value):
self._updatable_id = value
#updatable_id.deleter
def updatable_id(self):
del self._updatable_id
class Persons(object):
def __init__(self):
self._ids = []
def find_person_by_id(self, person_id):
# search by id
existing = [i for i in self._ids if i.person_id == person_id]
if not existing:
# create and append
existing_person = DataItem(id)
self._ids.append(existing_person)
else:
# assign to existing
existing_person = existing[0]
# return the object to be acted upon
return existing_person
So.. Would someone be able to assist me with furtherly developing the object so that I may be storing the data properly inside of each of its instances, please?
I would gladly appreciate all detailed suggestions (especially as soon as I am also able to understand them).
Thank you all in advance!

I finally developed what I was looking for.
A bit messy, but that does exactly what I need.
Thanks to #Robert Lee for the attempt, despite it was not what I chose to continue with.
class PersonData(object):
def __init__(self, email, country_code, phone_number, user_id, names):
self._email = email
self._country_code = country_code
self._phone_number = phone_number
self._user_id = user_id
self._names = names
self._started = []
self._ended = []
self._schedule_id = []
#property
def email(self):
return self._email
#property
def country_code(self):
return self._country_code
#property
def phone_number(self):
return self._phone_number
#property
def user_id(self):
return self._user_id
#property
def names(self):
return self._names
#property
def started(self):
return self._started
#started.setter
def started(self, started):
self._started.append(started)
#started.deleter
def started(self):
del self._started
#property
def ended(self):
return self._ended
#ended.setter
def ended(self, ended):
self._ended.append(ended)
#ended.deleter
def ended(self):
del self._ended
#property
def schedule_id(self):
return self._schedule_id
#schedule_id.setter
def schedule_id(self, schedule_id):
self._schedule_id.append(schedule_id)
#schedule_id.deleter
def schedule_id(self):
del self._schedule_id
class PeopleBuffer(object):
def __init__(self):
self._people = []
def find_by_id(self, email, country_code, phone_number, user_id, names):
# search by id
existing = [i for i in self._people if i.user_id == user_id]
if not existing:
# create and append if not found
existing_person = PersonData(email, country_code, phone_number, user_id, names)
self._people.append(existing_person)
else:
# assign to existing
existing_person = existing[0]
# return an object to be acted upon
return existing_person
def return_all(self):
for each_person in self._people:
print("each_person: ")
print("Email: %s" % each_person.email)
print("Country Code: %s" % each_person.country_code)
print("Phone Number: %s" % each_person.phone_number)
print("User Id: %s" % each_person.user_id)
print("Names: %s" % each_person.names)
print("Started: %s" % each_person.started)
print("Ended: %s" % each_person.ended)
print("ScheduleId: %s" % each_person.schedule_id)
class MainApplication(object):
def __init__(self):
self._buffer = PeopleBuffer()
def _populate_person(self, email, country_code, phone_number, user_id, names, started, ended, schedule_id):
person = self._buffer.find_by_id(email, country_code, phone_number, user_id, names)
person.started.append(started)
person.ended.append(ended)
person.schedule_id.append(schedule_id)
def _print_people(self):
self._buffer.return_all()
def main(self):
while input("Continue? ") != "No":
user_id = input("Enter UserId: ")
names = input("Enter Name: ")
started = input("Enter Started: ")
ended = input("Enter Ended: ")
schedule_id = input("Enter ScheduleId: ")
email = input("Enter Email: ")
country_code = input("Enter CountryCode: ")
phone_number = input("Enter PhoneNumber: ")
self._populate_person(email, country_code, phone_number, user_id, names, started, ended, schedule_id)
self._print_people()
def main():
app = MainApplication()
app.main()
if __name__ == '__main__':
main()

Based on what you are doing I would consider doing json data format and do something like this. Please excuse my quick and dirty code but I think fundamentally you are looking for a way to create a data format that might work for your scenario.
Looking over it one more time, I feel like this might be the format you are looking for
[
{
"person_id": "ID1234",
"name": "Anton Todorov",
"schedule": [
{
"schedule_id": "SCHEDID1",
"started_at": "2022-12-26T00:00:00+02:00",
"ended_at": "2022-12-26T02:00:00+02:00"
},
{
"schedule_id": "SCHEDID3",
"started_at": "2022-12-26T14:00:00+02:00",
"ended_at": "2022-12-27T02:00:00+02:00"
}
],
"contact_info": {
"email": "a.todorov#e-mail.email",
"country_code": 359,
"phone_number": "000000000"
}
},
{
"person_id": "ID5678",
"name": "Morgan Freeman",
"schedule": [
{
"schedule_id": "SCHEDID2",
"started_at": "2022-12-26T02:00:00+02:00",
"ended_at": "2022-12-26T14:00:00+02:00"
}
],
"contact_info": {
"email": "slogan#draftkings.com",
"country_code": 1,
"phone_number": "000000000"
}
}
]
Code:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import json
def main():
people = list()
person = dict()
person['person_id'] = 'ID1234'
person['name'] = 'Anton Todorov'
person['schedule'] = list()
schedule = dict()
schedule['schedule_id'] = 'SCHEDID1'
schedule['started_at'] = '2022-12-26T00:00:00+02:00'
schedule['ended_at'] = '2022-12-26T02:00:00+02:00'
person['schedule'].append(schedule)
schedule = dict()
schedule['schedule_id'] = 'SCHEDID3'
schedule['started_at'] = '2022-12-26T14:00:00+02:00'
schedule['ended_at'] = '2022-12-27T02:00:00+02:00'
person['schedule'].append(schedule)
contact_info = dict()
contact_info['email'] = 'a.todorov#e-mail.email'
contact_info['country_code'] = 359
contact_info['phone_number'] = '000000000'
person['contact_info'] = contact_info
people.append(person)
person = dict()
person['person_id'] = 'ID5678'
person['name'] = 'Morgan Freeman'
person['schedule'] = list()
schedule = dict()
schedule['schedule_id'] = 'SCHEDID2'
schedule['started_at'] = '2022-12-26T02:00:00+02:00'
schedule['ended_at'] = '2022-12-26T14:00:00+02:00'
person['schedule'].append(schedule)
contact_info = dict()
contact_info['email'] = 'slogan#draftkings.com'
contact_info['country_code'] = 1
contact_info['phone_number'] = '000000000'
person['contact_info'] = contact_info
people.append(person)
print(json.dumps(people, indent=4))
if __name__ == '__main__':
main()

First things first; Your example feels more like a Java example written in Python. Instance variables are in practice properties, so all of your #property methods is redundant code.
dataclasses module is implemented for cases like yours. For example,
Schedule data class:
# schedule.py
from dataclasses import dataclass, field
from datetime import datetime
from typing import Union
#dataclass(unsafe_hash=True)
class Schedule:
schedule_id: str
start: Union[str, datetime]
end: Union[str, datetime]
def __post_init__(self):
self.start = self._datetime_converter(self.start)
self.end = self._datetime_converter(self.end)
def _datetime_converter(self, dt: str):
return datetime.strptime(dt, '%Y-%m-%dT%H:%M:%S+02:00')
def overlaps_with(self, other: Schedule):
return not ((self.start >= other.end) ^ (other.start >= self.end))
#property
def deltatime(self):
return self.end - self.start
Worker data class:
# worker.py
from functools import reduce
from dataclasses import dataclass, field
from .schedule import Schedule
#dataclass
class Worker:
person_id: str
name: str
contacts: str = field(repr=False)
schedules: set[Schedule] = field(init=False, default_factory=set)
email: str = field(init=False, default='')
country: int = field(init=False, default=-1)
phone: str = field(init=False, default='')
def __post_init__(self):
self.email, self.country, self.phone = self.contacts
#classmethod
def add_from_json(cls, worker_id: str, file: str):
NotImplemented
def overlapping_schedules(self, schedule: Schedule):
return filter(lambda s: s.overlaps_with(schedule), self.schedules)
def schedule_overlaps(self, schedule: Schedule):
return any(self.overlapping_schedules(schedule))
def add_schedule(self, schedule: Schedule):
if not self.schedule_overlaps(schedule):
self.schedules.update({schedule})
else:
NotImplemented
def add_schedules_from_json(self, file: str):
NotImplemented
def remove_schedule(self, schedule: Schedule):
self.schedules -= {schedule}
#property
def total_time_scheduled(self):
deltas = map(lambda s: s.deltatime, self.schedules)
return reduce(lambda x, y: x + y, deltas)
def __hash__(self):
return hash(id(self.person_id))
def __eq__(self, other):
return self.person_id == other.person_id
In theory you don't really need to create a Workers class. The methods __hash__ and __eq__ take care of that and you just need to create a set[Worker]. However, if you still want to create a Workers class, just loop over the existing methods.

Related

Editable Callers for Class Attributes - Python

I am working on a mock Student Database using OOP in python, and I am trying to use a function to search for certain parameters of a Class.
In the following example, School is a large class that holds instances of Students as one of its arguments. Hence (For Student in School.Student)
found_list = []
class Student():
def __init__(self, name, age, gender, name_of_school, class_type):
self.name = name
self.age = age
self.gender = gender
self.name_of_school = name_of_school
self.class_type = "S"
Example_Student = Student("Joseph", 8, "male", "The School", "S")
gender_to_be_found = input("Enter the gender to be searched for ")
for Student in School.Student:
if Student.gender == gender_to_be_found:
found_list.append(Student)
This works as a principle but I am wanting to do it in the below format, so I can search for various attributes of a Class through one function
def search_for(value_to_be_found, values_to_searched_from, end_list, class_argument):
if end_list != values_to_searched_from:
for possible_targets in value_to_be_found:
for possible_matches in values_to_searched_from:
try:
if possible_targets == possible_matches.class_argument:
end_list.append(possible_matches)
except:
pass
else:
for possible_targets in value_to_be_found:
for possible_matches in values_to_searched_from:
try:
if possible_targets != possible_matches.class_argument:
end_list.remove(possible_matches)
except:
pass
so that I can pass in the (class_argument) "gender"
and automatically search for any Student.gender that matches my value_to_be_found
search_for("Joseph", School.Student, found_list, "name")
Clearly this proposed method (above) is non-functional, but I feel like there is a way to do this that I have not managed to quite achieve.
This error is produced:
AttributeError: object has no attribute 'class_argument'
Thanks in advance for any help :)
You could add a search function to School, using getattr to access attributes of an object:
class Student():
def __init__(self, name, age, gender, name_of_school, class_type):
self.name = name
self.age = age
self.gender = gender
self.name_of_school = name_of_school
self.class_type = "S"
class School:
def __init__(self):
self.students = []
def add(self, student):
self.students.append(student)
def search(self, value, search_attribute):
result = []
for s in self.students:
student_value = getattr(s, search_attribute, None)
if student_value == value:
result.append(s)
return result
s1 = Student("Joseph", 8, "male", "The School", "S")
s2 = Student("Joseph2", 9, "male", "The School", "S")
s3 = Student("Joseph3", 10, "male", "The School", "S")
s = School()
s.add(s1)
s.add(s2)
s.add(s3)
print(s.search("Joseph", "name"))
print(s.search(10, "age"))
print(s.search("gender", "binary"))
print(s.search("The School", "name_of_school"))
Out:
[<__main__.Student object at 0x107c19fd0>]
[<__main__.Student object at 0x107c19ee0>]
[]
[<__main__.Student object at 0x10ab16fd0>, <__main__.Student object at 0x10ab16fa0>, <__main__.Student object at 0x10ab16ee0>]

Wierd jsonpickle.encode behavior

I'm using jsonpickle as a database for my small project and encountered some weird behavior when encoding a complex class, basically Book from a User instance is encoded correctly but books in db list are encoded as strange {"py/id":4}-s.
Could someone please explain where I went wrong and how do I fix this
import jsonpickle
from random import randint
admin_ids = [302115492]
def gen_id():
return randint(1, 2**64)
class Book():
def __init__(self, name, author, img, oid):
self.name = name
self.author = author
self.img = img
self.id = gen_id()
self.owner_id = oid
class User():
id = 0
name = ""
books = []
requests = []
def __init__(self, name, uid):
self.id = uid
self.name = name
self.books = []
class Database():
def __init__(self):
self.u_dir = "users"
self.b_dir = "books"
self.users = []
self.books = []
def add_book(self, book):
self.books.append(book)
for user in self.users:
if(user.id == book.owner_id):
user.books.append(book)
def add_user(self, user):
self.users.append(user)
db=Database()
db.add_user(User('John',123))
db.add_book(Book('name','author','img.png',123))
print(jsonpickle.encode(db))
Output:
{
"py/object":"__main__.Database",
"u_dir":"users",
"b_dir":"books",
"users":[
{
"py/object":"__main__.User",
"id":123,
"name":"John",
"books":[
{
"py/object":"__main__.Book",
"name":"name",
"author":"author",
"img":"img.png",
"id":8045585124766781176,
"owner_id":123
}
]
}
],
"books":[
{
"py/id":4
}
]
}
So apparently jsonpickle uses this id thing to compactly store objects that are already stored somewhere inside object's encoding. To store all objects explicitly one can use make_refs flag when encoding. In the code above it would look like this: print(jsonpickle.encode(db), make_refs=False)

How iterate through a list of class attributes and change their values?

I am trying to create a function, that if the user did not enter any value in input field then it sets the text value to 0 or to any other number.
value_text = [self.trig_side_a_value.text, self.trig_side_b_value.text, self.trig_side_c_value.text, self.trig_angle_A_value.text, self.trig_angle_B_value.text, self.trig_angle_C_value.text]
for i in value_text:
if i == "":
i = "0"
else:
pass
Thanks in advance!
OK, so there is quite a bit wrong here. I will try to answer this with as much explanation as possible.
One thing that is wrong:
value_text = [self.trig_side_a_value.text, self.trig_side_b_value.text, self.trig_side_c_value.text, self.trig_angle_A_value.text, self.trig_angle_B_value.text, self.trig_angle_C_value.text]
for i in value_text:
if i == "":
i = "0"
This creates a list value_text and populates it with copies of the text attributes. Changing what is in the list does not affect the elements of self..text.
What needs to happen is that you need to iterate through the attributes of the class, setting each attributes' text member:
Here is a solution:
class TextContainer():
""" This is a class with just one attribute 'text' """
def __init__(self, text=""):
self.text = text
class TestClass():
""" This class contains a set of TextContainer objects as its attributes. """
def __init__(self):
self.trig_side_a_value = TextContainer("a")
self.trig_side_b_value = TextContainer("b")
self.trig_side_c_value = TextContainer()
self.trig_angle_A_value = TextContainer("A")
self.trig_angle_B_value = TextContainer("B")
self.trig_angle_C_value = TextContainer()
def test_me(self):
""" Test the setting of each attribute """
for attr, container in self.__dict__.items():
if not container.text:
container.text = "0"
def __str__(self):
""" Returns a string representation of the class """
return "TestClass: ({}, {}, {}, {}, {}, {})".format(
self.trig_side_a_value.text,
self.trig_side_b_value.text,
self.trig_side_c_value.text,
self.trig_angle_A_value.text,
self.trig_angle_B_value.text,
self.trig_angle_C_value.text)
def main():
test = TestClass()
test.test_me()
print(test)
if __name__ == '__main__':
main()
Hope that helps. Others, please correct my usage of the __dict__ - I'm some shaky ground here.

Printing classes in Python with multiple values

I have spent a good while searching through this website so I hope this question hasn't been asked before - apologises if it has. I'm learning classes for the first time and I'm making a class with multiple users (could be 50+ but for now, I just have 2 in my example). What I'm trying to do is have certain information about users/employees and be able to print them all in one go... in a way that isn't a complete eyesore! This is what I have attempted:
class User:
def __init__(self, user_id, first, last, address):
self.user_id = user_id
self.first = first
self.last = last
self.address = address
self.email = first + '.' + last + '#python.com'
def all_users(self):
print()
print('User ID: {} First Name: {} {} {} {}'.format(self.user_id, self.first, self.last, self.address, self.email))
print()
user_1 = User(123, 'Kim', 'N', 'London')
user_2 = User(312, 'Chris', 'E', 'Japan')
print(all_users(User))
This is the error message that I am receiving:
print('User ID: {} First Name: {} {} {} {}'.format(self.user_id, self.first, self.last, self.address, self.email))
AttributeError: type object 'User' has no attribute 'user_id'
Thanks in advance for any help or guidance.
Sounds like you want the User class to contain a list of all users.
This is called a class variable because it is attached to the class itself, instead of being attached to a particular instance of the class.
Example code:
class User(object):
users = []
def __init__(self, first, last):
self.first = first
self.last = last
# now that this instance is fully initialized, add it
# to the master list of users
User.users.append(self)
def __str__(self):
return '{} {}'.format(self.first, self.last)
#staticmethod
def all_users():
for user in User.users:
print (user)
user_1 = User('Kim', 'Smith')
user_2 = User('Chris', 'Jones')
User.all_users()
You should probably implement the __str__ or __repr__ special methods, which are designed to print human readable and "official" representations of the class instances, respectively
class User():
...
def __str__(self):
attrs = ['{}={}'.format(k, repr(v)) for k, v in self.__dict__.items()]
return '{}({})'.format(self.__class__.__name__, ', '.join(attrs))
Then it would look like this
>>> user = User('123', 'John', 'Doe', 'USA')
>>> print(user)
User(user_id='123', first='John', last='Doe', address='USA', email='John.Doe#python.com')

Deserialize the JSON string into object in Python

I wrote this code to save and load an object into JSON.
I faced to this error when I accessed into attribute:
I use python json class to serialize the object and deserialize it.
AttributeError: 'dict' object has no attribute 'weeknumber'
# -*- coding: utf-8 -*-
import pickle
import pprint
from datetime import date
import datetime
import decimal
import json
class Jsonable(object):
def __iter__(self):
for attr, value in self.__dict__.iteritems():
if isinstance(value, datetime.datetime):
iso = value.isoformat()
yield attr, iso
elif isinstance(value, decimal.Decimal):
yield attr, str(value)
elif(hasattr(value, '__iter__')):
if(hasattr(value, 'pop')):
a = []
for subval in value:
if(hasattr(subval, '__iter__')):
a.append(dict(subval))
else:
a.append(subval)
yield attr, a
else:
yield attr, dict(value)
else:
yield attr, value
class MyCalendar(Jsonable):
year = 0
weeks = {}
def __init__(self, _year,_weeks):
self.year = _year
self.weeks = _weeks;
class MyWeek(Jsonable):
weeknumber = 0
days = {}
def __init__(self, _week,_days):
self.weeknumber = _week
self.days = _days
class MyDay(Jsonable):
daynumber = 0
courses = {}
def __init__(self, _day,_courses):
self.daynumber = _day
self.courses = _courses
class MyCourse(Jsonable):
id = 0
title = ''
def __init__(self, _id,_title):
self.id = _id
self.title = _title
def init():
_myc = MyCalendar(date.today().year,[]);
for wi in range(0, 52):
_week = MyWeek(wi,[])
for di in range(0, 5):
_day = MyDay(di,[])
for ci in range(0, 4):
_course = MyCourse(ci,"empty")
_day.courses.append(_course)
_week.days.append(_day)
_myc.weeks.append(_week)
return _myc
def save(_t):
with open('data2.txt', 'w') as outfile:
json.dump(dict(_t), outfile)
def load():
_t = init();
with open('data2.txt', 'r') as infile:
_t.__dict__ = json.loads(infile.read())
return _t
def setCourse(_t,_weeknumnber,_dayumber,_course,_title):
_t.weeks[_weeknumnber].days[_dayumber].courses[_course].title = _title
return _t
_mycal = init();
setCourse(_mycal,1,2,3,"Python");
save(_mycal)
_mycal = load()
pprint.pprint( _mycal.weeks[1].weeknumber)
Your code is essentially correct, but you're mixing a lot of aspects and parsing the JSON in an ugly way that gives you the current results where you can't access the attributes directly but you treat the objects created as a dictionary and need to access it by key. To achieve your way, and keep the attributes in tact you'll need to create your own parser for the classes. I've made a quick copy of your code on my workspace:
import json
class MyCalendar:
def __init__(self, _year, _weeks):
self.year = _year
self.weeks = _weeks
def to_JSON(self):
# Dumps the JSON correctly, passing over the __dict__ of the class to assmble it.
return json.dumps(self, default=lambda o: o.__dict__)
class MyWeek:
def __init__(self, _week, _days):
self.weeknumber = _week
self.days = _days
class MyDay:
def __init__(self, _day, _courses):
self.daynumber = _day
self.courses = _courses
class MyCourse:
def __init__(self, _id, _title):
self.id = _id
self.title = _title
def json_calendar(dct):
year = dct['year'] # year, for quick reference.
weeks = {} # our weeks, as a dict.
for week in dct['weeks']: # iterate through the available weeks in the given dct.
days = {} # our days, as a dict.
for day in dct['weeks'][week]['days']: # iterate through the available days in the week.
courses = {} # our courses, as a dict.
for course in dct['weeks'][week]['days'][day]['courses']: # iterate through the available courses in the day.
courseId = int(course) # course id
courseTitle = dct['weeks'][week]['days'][day]['courses'][course]['title'] # course title
courses[courseId] = MyCourse(courseId, courseTitle) # Assemble our courses into the courses dict.
days[int(day)] = MyDay(day, courses) # Assemble our days into the day dict.
weeks[int(week)] = MyWeek(week, days) # Assemble our weeks into the weeks dict.
return MyCalendar(year, weeks) # Assemble and return our calendar.
Now you can simply create your calendar, and now when you're going to dump it you just need to do _mycal.To_JSON() and to get the calendar object from the JSON you need to do _mycal = json_calendar(json_data) and you can access the attributes without any issues since we parsed it in our specific parser.

Categories