Adding Tickets Relationship with Discount Codes in Open Event Server

Recently (as of this writing), it was discovered that the relationship between discount codes and tickets was not implemented yet in Open Event Server. It turns out that the server has two types of discount codes - discount codes for entire events and discount codes for individual tickets of a specific event. More information on how discount code themselves are implemented in the server can be found in this blog post from 2017 - Discount Codes in Open Event Server. So, for implementing the relationship of discount codes with tickets, it was decided to be present only for discount codes that have the DiscountCodeSchemaTicket schema, since those are the discount codes that are used for individual tickets. As a first step, the `tickets` attribute of the discount code model was removed, as it was redundant. The already implemented used_for attribute did the same job, and with better validation. At the same time, discount code was added as an attribute. In the ticket model file: discount_code_id = db.Column(db.Integer, db.ForeignKey('discount_codes.id', ondelete='CASCADE')) discount_code = db.relationship('DiscountCode', backref="tickets") Also, in the __init__ constructor: def __init__(self, ..., discount_code_id=None, ...): ... ... self.discount_code_id = discount_code_id After that, we added a discount_code_id field in the ticket schema file: discount_code_id = fields.Integer(allow_none=True) In this file, we also removed the redundant tickets field. Now, we migrated the Open Event Server database via the following commands: $ python manage.py db migrate then $ python manage.py db upgrade Next, in the discount code schema file, we added the tickets relationship. Note that this is a one-to-many relationship. One discount code (for tickets) can be mapped to many tickets. Here is the code for that relationship, in the discount code schema file, under the DiscountCodeSchemaTicket class: tickets = Relationship(attribute='tickets', self_view='v1.discount_code_tickets', self_view_kwargs={'id': '<id>'}, related_view='v1.ticket_list', related_view_kwargs={'discount_code_id': '<id>'}, schema='TicketSchemaPublic', many=True, type_='ticket') For this, we, of course, imported the TicketSchemaPublic in this file first. After that, we created a DiscountCodeTicketRelationship class in the discount codes API file: class DiscountCodeTicketRelationship(ResourceRelationship): """ DiscountCode Ticket Relationship """ decorators = (jwt_required,) methods = ['GET', 'PATCH'] schema = DiscountCodeSchemaTicket data_layer = {'session': db.session, 'model': DiscountCode} The next step was to add the query code to fetch the tickets related to a particular discount code from the database. For this, we added the following snippet to the query() method of the TicketList class in the tickets API file: if view_kwargs.get('discount_code_id'): discount_code = safe_query(self, DiscountCode, 'id', view_kwargs['discount_code_id'], 'discount_code_id') # discount_code - ticket :: one-to-many relationship query_ = self.session.query(Ticket).filter_by(discount_code_id=discount_code.id) The only thing that remains now is adding the API routes for this relationship. We do that in the project’s __init__.py file: api.route(TicketList, 'ticket_list', '/events/<int:event_id>/tickets', '/events/<event_identifier>/tickets', '/ticket-tags/<int:ticket_tag_id>/tickets', '/access-codes/<int:access_code_id>/tickets', '/orders/<order_identifier>/tickets', '/discount-codes/<int:discount_code_id>/tickets') … api.route(DiscountCodeTicketRelationship, 'discount_code_tickets', '/discount-codes/<int:id>/relationships/tickets')   Many routes already map to TicketList, we added one for that comes from discount codes API. Now we can use Postman to check this relationship, and it indeed works as expected, as seen below! Here’s the end: References: Discount Codes in Open Event Server flask-rest-jsonapi Docs SQLAlchemy Docs

Continue ReadingAdding Tickets Relationship with Discount Codes in Open Event Server

Migrating Event Ratings of Open Event with Stored Procedures

Many developers know about procedural languages and have used them in some form or another, but this is really an unpopular tool, despite its power. There are many advantages (and few disadvantages) of these languages, which we will learn about soon. Having a right amount of database-stored procedure code with the help of these languages can really enhance the speed and responsiveness of an application. This article will teach us how procedural languages can be utilized in database management and how they were used recently for a bug fix in Open Event Server. PostgreSQL, like any other powerful, relational database management system (RDBMS), provides the functionality to create and use stored procedures. Essentially, a stored procedure is database logic code which is saved on the database server. This code can be executed directly in the database, and can (and is!) often used to shift business logic from the application layer of a software to the database layer. This simple shift often has many advantages - including faster execution (as code executes at a lower stack level) and better security. When firing database queries from the application layer (i.e., the code that programmers write for storing programmable objects, performing business logic and so on), it often happens that parameters from the programming language itself are passed in to SQL, which then generates a complete SQL query. For example, here’s how a novice query might look like: import psycopg2 conn = psycopg2.connect(dbname="oevent", user="john", password="start") cur = conn.cursor() name = "Sam" cur.execute("SELECT * FROM users WHERE name='%s'" % name) # DANGEROUS! This is an extremely “exposed” code that can be exploited for malicious access, with a technique called SQL injection. This technique essentially “injects” malicious code via these passed parameters, like the variable name mentioned in the above code. With having stored procedures for business logic, there is no room for SQL injection. They solve this problem by writing the query beforehand, and having the parameterized data as a different entity. The pre-processed query within the corresponding stored procedure now looks like SELECT * FROM users WHERE name=?   The database driver sends the name of this stored procedure (or, in standard parameterised queries, just the query text itself) and a list of parameters, as distinct separate entities in the protocol. More details on how stored procedures enhance security can be found here. After learning so much about the advantages of stored procedures (which are enabled by procedural languages), let’s write one! Postgres supports multiple languages for writing stored procedures; here we will use PL/pgSQL, which is the most popular choice for Postgres. This procedural language, inspired (heavily) by Oracle’s PL/SQL language, looks very similar to SQL. To use this procedural language, we have to first install it. In Postgres, procedural languages are installed per-database, not server-wide. We can use the popular Postgres client psql for this purpose, or simply the createlang command on the command line: $ createlang plpgsql yourdb   Now let’s create a simple procedure that prints the corresponding grades…

Continue ReadingMigrating Event Ratings of Open Event with Stored Procedures

Implementing Event Average Rating with SQLAlchemy

While implementing Open Event Server version 2, we decided to have a better way of ranking events by their quality. To define the “quality” of events, the programmers decided to accumulate the feedbacks of specific events and take the average of the ratings involved. Thus, the average rating of an event proves to be a good (enough) measure of its quality. While there are many ways to implement aggregate relationships in an app, here I demonstrate a rather modern methodology which insists on storing such aggregates once they’re computed. Since there is always a space-time/computation tradeoff in software development, this task was no exception. At first, the straightforward idea that came to my mind was to query the Postgres database every time a request for average rating was made. This sounds simple, but with hundreds of events stored on a server, and potentially thousands of users querying for events, this seemed to be a computationally expensive approach. It was costly because the average rating aggregate would be computed for each request, and there could potentially be thousands of such concurrent requests. Therefore, a better idea is to compute the aggregate once, store it in the database (compromising space in the tradeoff mentioned above, but saving a large amount of computation at the same time), and update only when a change is made. In our specific case, the update should happen only when a new rating is added, a rating is deleted or an existing rating is modified. Since the advantages outnumbered the disadvantages, this was the strategy to be implemented. The first step in implementing average rating was to modify the database model of events accordingly. For this, I performed the necessary imports in the events’ database model file: from sqlalchemy_utils import aggregated from app.models.feedback import Feedback Now comes the tricky part. We want an average_rating column in the events table, that contains the mean rating of events. The values in this column should be updated every time a change is made to the feedbacks table. To perform this sort of functionality, the best, raw tool is a Postgres trigger. A trigger should be created that is fired after every update to the feedbacks table, which should update the average rating values in the events table. Here’s how the raw code of such a trigger looks like: create or replace function UpdateAverageRating() returns trigger AS $$ BEGIN UPDATE events SET average_rating=( SELECT avg(rating) FROM feedbacks WHERE event_id=NEW.event_id GROUP BY event_id ) WHERE id = NEW.event_id END $$ language plpgsql Fortunately, the translation of such a trigger into SQLAlchemy-speak is not only easy, but also very elegant. The imports I showed above already set the context for this translation. The event model class looks like the following: class Event(db.Model): """Event object table""" __tablename__ = 'events' __versioned__ = { 'exclude': ['schedule_published_on', 'created_at'] } id = db.Column(db.Integer, primary_key=True) identifier = db.Column(db.String) name = db.Column(db.String, nullable=False) external_event_url = db.Column(db.String) … … … The list of attributes continues, and to the end of this list,…

Continue ReadingImplementing Event Average Rating with SQLAlchemy

Enforcing Constraints Throughout a Flask Back-End

Recently it was discovered that Open Event Server does not validate attendees’ tickets. Specifically, it was possible to create an arbitrary number of attendees who’d be attending an event on the same ticket! To fix this, a constraint had to be set up across different layers of Open Event Server, which is based on Flask and Postgres. This post will demonstrate how the constraint was added in the server, and these steps should apply in general to any Flask-based server with a relational back-end. First of all, the immediate idea that comes after investigating such an issue, is to add a UNIQUE constraint to the database. For this specific case, the problem was in ticket_holders table of the Open Event database. There was originally no check imposed on the ticket_id and event_id columns. As can be seen in the ticket_holders schema (using the \d+ ticket_holders command), there is no mention of uniqueness on either column. The initial guess was that the combination of ticket_id and event_id should be unique throughout the table to avoid multiple holders attending on the same ticket. However,imposing uniqueness on just the ticket_id column would’ve also worked. So, to be on the safer side, I moved ahead by adding uniqueness on both the columns. To fix this, we need to make changes to the ticket_holder model. So, in the ticket_holder model file, we add a __table_args__ attribute to the TicketHolder class. This attribute represents the various constraints imposed on the ticket_holders table: class TicketHolder(db.Model): __tablename__ = "ticket_holders" __table_args__ = ( db.UniqueConstraint('ticket_id', 'event_id', name='ticket_event'), ) # this is the constraint we add id = db.Column(db.Integer, primary_key=True) firstname = db.Column(db.String, nullable=False) lastname = db.Column(db.String, nullable=False) … … … The TicketHolder class has attributes named ticket_id and event_id, so to add a unique constraint over them, we pass their names to the UniqueConstraint constructor. Also, any suitable name can be given to the constraint, I chose ‘ticket_event’ to simply emphasize the relationship. Now that we’ve edited the database model file, we have to perform a database migration. Before we command the migration, we have to remove the entries that potentially violate the constraint we just imposed. As a temporary fix, I connected to the database and deleted all non-unique rows via plain SQL. For a more consistent fix, I will implement this simple deletion code in the database migration file, if need be. So, once the non-unique rows are gone, we perform the database migration as follows: $ python manage.py db migrate And then, $ python manage.py db upgrade These commands may be different for different projects, but their purpose is the same - to update the database. The upgrade command generates a migration file which looks as follows: from alembic import op import sqlalchemy as sa import sqlalchemy_utils # revision identifiers, used by Alembic. revision = '9d21de792967' down_revision = '194a5a2a44ef' def upgrade(): op.create_unique_constraint('ticket_event', 'ticket_holders', ['ticket_id', 'event_id']) def downgrade(): op.drop_constraint('ticket_event', 'ticket_holders', type_='unique') We can see that the upgrade() function has the command for adding our constraint. Once the database…

Continue ReadingEnforcing Constraints Throughout a Flask Back-End

Adding System Messages on Open Event Server

The Open Event Server enables organizers to manage events from concerts to conferences and meetups. It offers features for events with several tracks and venues. Event managers can create invitation forms for speakers and build schedules in a drag and drop interface. The event information is stored in a database. The system provides API endpoints to fetch the data, and to modify and update it. The Open Event Server is based on JSON 1.0 Specification and hence build on top of Flask Rest Json API (for building Rest APIs) and Marshmallow (for Schema). In this blog, we will talk about how to add API for accessing the System Messages on Open Event Server. The focus is on its Model updation and it’s Schema creation. Model Updation For the System Messages, we’ll make update model as follows Now, let’s try to understand this Schema. In this feature, we are providing Admin the rights to read email and notification formats used in Open Event application. First of all, there is the need to know that it has three columns notification_status, user_control_status and mail_status of type boolean. Next it has action attribute which is of type String. At last, we have hybrid properties email_message and notification_message which will return the format of email and notification respective to the action string. The hybrid properties depends on _email_message method and _notification_message method. These methods reads the MAILS and NOTIFS dictionaries and return there values corresponding to string of action key of corresponding record. Schema Creation For the System Messages, we’ll make our Schema as follows Now, let’s try to understand this Schema. In this feature, we are providing Admin the rights to read email and notification formats used in Open Event application. First of all, there is the need to know that it has three boolean properties notification_status, user_control_status and mail_status Next it has action attribute which is of type String and it’s value can be validated to have any one of the list provided in choices. At last, it has the String attributes email_message and notification_message which will return the action formats of email and notification concerning the action string provided. So, we saw how System Messages Schema and Model is created / updated to allow Admin users to read it’s values. Resources Documentation | Marshmallow : https://marshmallow-jsonapi.readthedocs.io/en/latest/ Documentation | Flask Rest JSONAPI : http://flask-rest-jsonapi.readthedocs.io/en/latest/

Continue ReadingAdding System Messages on Open Event Server

Adding Dredd Tests for Image Sizes on Open Event Flask Server

In this blog, we will talk about how to add dredd hooks for testing the API of Event Image Sizes and Speaker Image Sizes in Open Event Server. The focus is on adding the factory class and dredd hooks of these APIs using factory-boy python library and Dredd API testing framework. Factory Creation For the Event and Speaker Image Sizes, we’ll make our factory classes EventImageSizeFactory  and SpeakerImageSizeFactory as follows Now, let’s try to understand this class. In this class, we are writing the sample data two records of ImageSizes Model, these records corresponds to Event and Speaker Image Sizes. First of all, we inherit class factory.alchemy.SQLAlchemyModelFactory to build our sample data which for Image Sizes. Class Meta has model and sqlalchemy_session attributes. Model tells the factory class of to which model this factory class push the data to database and sqlalchemy_session is assigned with the current database session. Next, we add the attributes according to the model and Schema of Image Sizes. Adding Dredd Hooks For the ImageSizes, we’ll make our dredd hooks as follows Now, let’s try to understand these tests. In this tests, we check the API by matching the response after adding a record in these API to one which is present at API blueprint. First of all, we use decorator @hooks.before which means we first add a record in the database and then match the response we get from API say /v1/event-image-sizes with the response mentioned at Image Size > Event Image Size Details > Get Event Image Size Details in API blueprint. We create an instance of EventImageSizeFactory which is a record of model Image Sizes. This record is then returned as a response of API /v1/event-image-sizes and matches with the blueprint at Image Size > Event Image Size Details > Get Event Image Size Details Similarly, we have added other dredd tests for PATCH method as well. So, we saw how factory-boy python library and Dredd API testing framework helped us in testing the REST APIs on Open Event Server. Resources Documentation | Dredd API testing framework: http://dredd.org/en/latest/ Documentation | Factory-boy: http://factoryboy.readthedocs.io/en/latest/

Continue ReadingAdding Dredd Tests for Image Sizes on Open Event Flask Server

Adding Event Roles Permission API on Open Event Server

The Open Event Server enables organizers to manage events from concerts to conferences and meetups. It offers features for events with several tracks and venues. Event managers can create invitation forms for speakers and build schedules in a drag and drop interface. The event information is stored in a database. The system provides API endpoints to fetch the data, and to modify and update it. The Open Event Server is based on JSON 1.0 Specification and hence build on top of Flask Rest Json API (for building Rest APIs) and Marshmallow (for Schema). In this blog, we will talk about how to add API for accessing and updating the events role permissions on Open Event Server. The focus is on Schema creation and it’s API creation. Schema Creation For the Events Role Permission, we’ll make our Schema as follows   Now, let’s try to understand this Schema. In this feature, we are providing Admin the rights to get and update the permission given to a role concerning a service. First of all, we are provide the four fields in this Schema, which are can_create, can_read, can_update and can_delete which are Boolean. All these fields gives us idea whether a user with a role can create, read, update and delete a service or not respectively in the whole system. Next there is a relationship with role which is one of organizer, coorganizer, track_organizer, moderator, registrar or attendee. Next there is a relationship with service which is one of Track, Microlocation, Session, Speaker or Sponsor. API Creation For the Events Role Permissions, we’ll make our API as follows Now, let’s try to understand this API. In this feature, we are providing Admin the rights to get and update the permission given to a role concerning a service. First of all, there is the need to know that this API has two method GET and PATCH. Decorators shows us that only Admin has permissions to access PATCH method for this API i.e. only Admins can modify the events role permissions . In EventsRolePermissionList, we are inheriting ResourceList from Flask Rest JSONAPI which will allow us to get all the records for the model Permission. In EventsRolePermissionDetail, we are inheriting ResourceDetail from Flask Rest JSONAPI which will allow us to get and update attributes of a record of model Permission. In EventsRolePermissionRelationship, we are inheriting ResourceRelationship from Flask Rest JSONAPI which will allow us to get and update relationships of a record of model Permission. So, we saw how Events Role Permission Schema and API is created to allow users to get it’s values and Admin users to modify it’s attributes and relationships. Resources Documentation | Marshmallow : https://marshmallow-jsonapi.readthedocs.io/en/latest/ Documentation | Flask Rest JSONAPI : http://flask-rest-jsonapi.readthedocs.io/en/latest/

Continue ReadingAdding Event Roles Permission API on Open Event Server

Building the API of Speaker Image Size on Open Event Server

The Open Event Server enables organizers to manage events from concerts to conferences and meetups. It offers features for events with several tracks and venues.It uses the JSON 1.0 Specification and build on top of Flask Rest Json API (for building Rest APIs) and Marshmallow (for Schema). In this blog, we will talk about how to add API for accessing and updating the Speaker Image Size on Open Event Server. The focus is on its API creation. API Creation For the SpeakerImageSizeDetail, we’ll make our Schema as follows Now, let’s try to understand SpeakerImageSizeDetail. In this feature, we are providing Admin the rights to Get and Update the SpeakerImageSizes kwargs['id'] = 2 states that Image Size model has 2 records and 1st record is used for Event Image Size and 2nd record is used for Speaker Image Size. decorators = (api.has_permission('is_admin', methods="PATCH", id="2"),) states that for Speaker Image Size, Update API is accessible to Admins only. methods = ['GET', 'PATCH'] states that this API provides two methods i.e. GET and PATCH. schema = SpeakerImageSizeSchema states that the schema which is used to return the response is Speaker Image Size Schema. data_layer = {'session': db.session, 'model': ImageSizes} states the session and Model used to fetch the records. Resources Documentation | Marshmallow : https://marshmallow-jsonapi.readthedocs.io/en/latest/ Documentation | Flask Rest JSONAPI : http://flask-rest-jsonapi.readthedocs.io/en/latest/

Continue ReadingBuilding the API of Speaker Image Size on Open Event Server

Add More Languages to a Skill in SUSI.AI

The SUSI SKill CMS provides skills in multiple languages. Often there are similar skills in different languages. For example, there is a News skill in English and Samachar skill in Hindi. Then why not link them together and mark one as the translation of the other. This will help the user to reach and explore the desired skill in an efficient way. Moreover, it may be easier to type ‘News’ than ‘समाचार’ and find the required skill through translations. So here it has been explained how to link two SUSI skills as translations. Server side implementation Create a skillSupportedLanguages.json file to store the related skills together as translations and make a JSONTray object for that in src/ai/susi/DAO.java file. The JSON file contains the language name and the skill name in that language, wrapped in an array. public static JsonTray skillSupportedLanguages; Path skillSupportedLanguages_per = skill_status_dir.resolve("skillSupportedLanguages.json"); Path skillSupportedLanguages_vol = skill_status_dir.resolve("skillSupportedLanguages_session.json"); skillSupportedLanguages = new JsonTray(skillSupportedLanguages_per.toFile(), skillSupportedLanguages_vol.toFile(), 1000000); OS.protectPath(skillSupportedLanguages_per); OS.protectPath(skillSupportedLanguages_vol); Now create an API that accepts the skill details and translation details and stores them in the JSON file. Create UpdateSupportedLanguages.java class for the API. Endpoint: /cms/updateSupportedLanguages.json Minimum user role: Anonymous Params: Model Group Language (language of the skill for which translation is to be added) Skill (name of the skill for which translation is to be added) New language (translation language of the skill) New skill name (name of the skill in translated language) When a new translation is added check if it already exists in the translation group stored in the skillSupportedLanguages.json. Use the DAO object and loop over the array, check is the language name and the language name already exists. If yes then simply return. if (!alreadyExixts) { groupName.put(createSupportedLanguagesArray(language_name, skill_name, new_language_name, new_skill_name)); } Otherwise, create a new object containing the new language name and the skill name in that language and add it to the translation group. public JSONArray createSupportedLanguagesArray(String language_name, String skill_name, String new_language_name, String new_skill_name) { JSONArray supportedLanguages = new JSONArray(); JSONObject languageObject = new JSONObject(); languageObject.put("language", language_name); languageObject.put("name", skill_name); supportedLanguages.put(languageObject); JSONObject newLanguageObject = new JSONObject(); newLanguageObject.put("language", new_language_name); newLanguageObject.put("name", new_skill_name); supportedLanguages.put(newLanguageObject); return supportedLanguages; } Add this API to SusiServer.java // Add translation to the skill UpdateSupportedLanguages.class Resources JSONTray - https://dev.susi.ai/javadoc/index.html?ai/susi/json/JsonTray.html  Wikipedia (list of languages with their ISO 639-1 codes) - https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes  

Continue ReadingAdd More Languages to a Skill in SUSI.AI

How webclient Settings are Implemented in SUSI.AI Accounts

The goal of accounts.susi is to implement all settings from different clients at a single place where user can change their android, iOS or webclient settings altogether and changes should be implemented directly on the susi server and hence the respective clients. This post focuses on how webclient settings are implemented on accounts.susi. SUSI.AI follows the accounting model across all clients and every settings of a user are stores at a single place in the accounting model itself. These are stored in a json object format and can be fetched directly from the ListUserSettings.json endpoint of the server. (more…)

Continue ReadingHow webclient Settings are Implemented in SUSI.AI Accounts