Open Event Server Ticket PDF: Where and Where-not to use static frame in xhtml2pdf

One among the very important features of Open Event Server project is the tickets sales feature, where a user can buy a number of different tickets for a number of people after which he is given a link to download the ticket pdf. However, an issue concerning our Open Event Server project was that if a buyer bought different tickets at a time with different individual ticket holders, all tickets contained the same name, type and QR-Code, which in no way was acceptable since tickets and holders were different. We use the xhtml2pdf facility in order to convert an html to pdf. An xhtml2pdf facility helps us in converting HTML contents into PDF without the use of browser ‘print’ facility. In order to do this, we use the help of pages and frames, pages being the page of the PDF document, while a frame being that part of area within the page where the contents get stored. What is a PDF and HTML? The basic understanding of a PDF and an HTML is that, a PDF or Portable Document Format has layout such that it is measured in terms of specific width and height. However, for an HTML or Hyper Text Markup Language, we do no not have those specific widths and heights. Rather an HTML’s width depends on a person’s device of view and height can be infinitely long as desired. In terms of xhtml2pdf relation with pages and frames layout, we can identify with this diagram: +-page---------------------+ |                                    | |  +-content_frame-+  | |  |                          |    | |  |                          |    | |  |                          |    | |  |                          |    | |  +---------------------+   | |                                    | +----------------------------+ The stated issue was due to static frame in xhtml2pdf. What is that you ask? Static frames vs Content frames xhtml2pdf uses the concept of Static Frames to define content that remains the same across different pages (like headers and footers), and uses Content Frames to position the to-be-converted HTML content. Static Frames are defined through use of the @frame property -pdf-frame-content. Regular HTML content will not flow through Static Frames. Content Frames are @frame objects without this property defined. Regular HTML content will flow through Content Frames.(xhtml2pdf documentation) So, the basic idea of the use of  -pdf-frame-content  was to make the contents of the pdf static i.e. without having to continuously change alignments of the page. It made the whole pdf stay static. This actually caused the whole pdf content to stay constant, event while the loop was going over different name, QR-code and ticket-name values. That means the first loop content stayed over even with changes in values. Just a simple fix of not making the frame static was the solution. Here is a few insight of…

Continue ReadingOpen Event Server Ticket PDF: Where and Where-not to use static frame in xhtml2pdf

Documenting Open Event API Using API-Blueprint

FOSSASIA's Open Event Server API documentation is done using an api-blueprint. The API Blueprint language is a format used to describe API in an API blueprint file, where a blueprint file (or a set of files) is such that describes an API using the API Blueprint language. To follow up with the blueprint, an apiary editor is used. This editor is responsible for rendering the API blueprint and printing the result in user readable API documented format. We create the API blueprint manually. Using API Blueprint:- We create the API blueprint by first adding the name and metadata for the API we aim to design. This step looks like this :- FORMAT: V1 HOST: https://api.eventyay.com # Open Event API Server The Open Event API Server # Group Authentication The API uses JWT Authentication to authenticate users to the server. For authentication, you need to be a registered user. Once you have registered yourself as an user, you can send a request to get the access_token.This access_token you need to then use in Authorization header while sending a request in the following manner: `Authorization: JWT <access_token>` API blueprint starts with the metadata, here FORMAT and HOST are defined metadata. FORMAT keyword specifies the version of API Blueprint . HOST defines the host for the API. The heading starts with # and the first heading is regarded as the name of the API. NOTE - Also all the heading starts with one or more # symbol. Each symbol indicates the level of the heading. One # symbol followed by heading serves as the top level i.e. one # = Top Level. Similarly for  ## = second level and so on. This is in compliance with normal markdown format.         Following the heading section comes the description of the API. Further, headings are used to break up the description section. Resource Groups: -----------------------------     By using group keyword at the starting of a heading , we create a group of related resources. Just like in below screenshot we have created a Group Users. # Group Users For using the API you need(mostly) to register as an user. Registering gives you access to all non admin API endpoints. After registration, you need to create your JWT access token to send requests to the API endpoints. | Parameter | Description | Type | Required | |:----------|-------------|------|----------| | `name` | Name of the user | string | - | | `password` | Password of the user | string | **yes** | | `email` | Email of the user | string | **yes** |   Resources: ------------------     In the Group Users we have created a resource Users Collection. The heading specifies the URI used to access the resource inside of the square brackets after the heading. We have used here parameters for the resource URI which takes us into how to add parameters to the URI. Below code shows us how to add parameters to the resource URI. ## Users Collection…

Continue ReadingDocumenting Open Event API Using API-Blueprint

Open Event Server: Working with Migration Files

FOSSASIA's Open Event Server uses alembic migration files to handle all database operations and updations.  From creating tables to updating tables and database, all works with help of the migration files. However, many a times we tend to miss out that automatically generated migration files mainly drops and adds columns rather than just changing them. One example of this would be: def upgrade(): ### commands auto generated by Alembic - please adjust! ### op.add_column('session', sa.Column('submission_date', sa.DateTime(), nullable=True)) op.drop_column('session', 'date_of_submission') Here, the idea was to change the has_session_speakers(string) to is_session_speakers_enabled (boolean), which resulted in the whole dropping of the column and creation of a new boolean column. We realize that, on doing so we have the whole data under  has_session_speakers lost. How to solve that? Here are two ways we can follow up: op.alter_column: ---------------------------------- When update is as simple as changing the column names, then we can use this. As discussed above, usually if we migrate directly after changing a column in our model, then the automatic migration created would drop the old column and create a new column with the changes. But on doing this in the production will cause huge loss of data which we don’t want. Suppose we want to just change the name of the column of start_time to starts_at. We don’t want the entire column to be dropped. So an alternative to this is using op.alter_column. The two main necessary parameters of the op.alter_column is the table name and the column which you are willing to alter. The other parameters include the new changes. Some of the commonly used parameters are: nullable – Optional: specify True or False to alter the column’s nullability. new_column_name – Optional; specify a string name here to indicate the new name within a column rename operation. type_ – Optional: a TypeEngine type object to specify a change to the column’s type. For SQLAlchemy types that also indicate a constraint (i.e. Boolean, Enum), the constraint is also generated. autoincrement –  Optional: set the AUTO_INCREMENT flag of the column; currently understood by the MySQL dialect. existing_type– Optional: a TypeEngine type object to specify the previous type. This is required for all column alter operations that don’t otherwise specify a new type, as well as for when nullability is being changed on a column. So, for example, if you want to change a column name from “start_time” to “starts_at” in events table you would write: op.alter_column(‘events’, ‘start_time’, new_column_name=’starts_at’) def upgrade(): ### commands auto generated by Alembic - please adjust! ### op.alter_column('sessions_version', 'end_time', new_column_name='ends_at') op.alter_column('sessions_version', 'start_time', new_column_name='starts_at') op.alter_column('events_version', 'end_time', new_column_name='ends_at') op.alter_column('events_version', 'start_time', new_column_name='starts_at') Here, session_version and events_version are the tables name altering columns start_time to starts_at and end_time to ends_at with the op_alter_column parameter new_column_name. op.execute: -------------------- Now with alter_column, most of the alteration in the column name or constraints or types is achievable. But there can be a separate scenario for changing the column properties. Suppose I change a table with column “aspect_ratio” which was a string column and had values “on” and…

Continue ReadingOpen Event Server: Working with Migration Files

Open Event Server: Dealing with environment variables

FOSSASIA's Open Event Server uses some environment variables to maintain different configuration for different deployment. However one con of it was, for local development, developers have to run many export commands. It all started with the “It is not convenient, we humans are lazy.” phrase. A recent suggestion was to read this environment variables with files rather than running commands every time. This few example environment variable commands were: export DATABASE_URL=postgresql://open_event_user:test@127.0.0.1:5432/test and export INTEGRATE_SOCKETIO=false   Open Event Server now uses .env file and envparse to handle this. The changes were: We store the variables in .env file, let it get ignored by git, and Use envparse to parse the file. What is envpase:        envparse is a simple utility to parse environment variables. It aims to eliminate duplicated, often inconsistent parsing code and instead provide a single, easy-to-use wrapper. So instead of reading the string from the command line everytime: export DATABASE_URL=postgresql://open_event_user:password@127.0.0.1:5432/open_event_db We read it from the file: To read from command line we had: SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL', None) Which now was parsed using envparse as: SQLALCHEMY_DATABASE_URI = env('DATABASE_URL', default=None) In the above example, DATABASE_URL is a string and we store this string in SQLALCHEMY, setting the default by the default parameter(None in this case). The type of environment variable is originally string, we can simply use env to parse these strings. For simplicity and understanding we can also use env.str. For different types, we can simply type cast them like env.bool (for boolean), env.int (for integer) etc. One good advantage of this envparse feature is the direct use of these type cast rather than checking for strings (i.e. reading the string from command line). For example, earlier we had something like this: socketio_integration = os.environ.get('INTEGRATE_SOCKETIO’) if socketio_integration == true’: INTEGRATE_SOCKETIO =True Using envparse, we can have something like this: INTEGRATE_SOCKETIO = env.bool('INTEGRATE_SOCKETIO’, default=False) This helps in parsing as boolean instead of string. So, if we have INTEGRATE_SOCKETIO=true in .env file, we can write INTEGRATE_SOCKETIO = env.bool('INTEGRATE_SOCKETIO', default=False). This automatically converts the true to boolean True in python. In order to use envparse we install envparse with: $ pip install envparse In the case of Open Event Server project, you only have to install the requirements.txt and copy the .env.example already mentioned in installation doc. This is how we finally remove the headache of writing big exports again and again and fairly helps beginners to start off with Open Event Server  project much more easily and quickly.

Continue ReadingOpen Event Server: Dealing with environment variables

Sorting language-translation in Open Event Server project using Jinja 2 dictsort.

Working on the Open Event Server project an issue about arranging language-translation listing in alphabetical order came up. To solve this issue of language listing arrangement i.e. #2817, I found the 'd0_dictsort' function in jinja2 to sort dictionaries. It is a defined in jinja2.filters. Python dicts are unsorted and in our web application we at times may want to order them by either their key or value. So this function comes handy. This is what the function looks like: do_dictsort(value, case_sensitive=False, by='key') We can write them in three ways as: {% for record in my_dictionary|dictsort %}    case insensitive and sort the dict by key {% for record in my_dictionary|dicsort(true) %}    case sensitive and sort the dict by key {% for record in my_dictionary|dictsort(false, 'value') %}    sort the dict by value, normally sorted and case insensitive       The first way is easily understood that dict has been sorted by key not taking case into consideration. It is just in the same way written as dictsort(false).       Second way is basically the first being case sensitive. dictsort(true) here tells us that case is sensitive.      Third way is dictsort(false,'value'). The first parameter defines that case insensitive while second parameter defines that it is sorted by 'value'. The issues was to sort translation selector for the page in alphabetical order. The languages were stored in a dictionary which to change in order, I found this function very easy and useful. Basically what we had was: This is how the function was used in the code for the sort. Like this: <ul class="dropdown-menu lang-list"> {% for code in all-languages|dictsort(false,'value') %} <li><a href="#" style="#969191" class="translate" id="{{ code[0] }}">{{ all_languages[code[0]] }}<>a><li> {% endfor %} <ul> Here: {{ all_languages }} is the list which contained the languages like French, English, etc., which could be accessed with its global language code. code here(index for all_languages) is a tuple of {'global_language_code','language'} (An example would be ('fr','French'), so code[0] gave me the language_code. Finally, the result: This is one of the simple ways to sort your dictionaries.

Continue ReadingSorting language-translation in Open Event Server project using Jinja 2 dictsort.

Open Event Server: No (no-wrap) Ellipsis using jquery!

Yes, the title says it all i.e., Enabling multiple line ellipsis. This was used to solve an issue to keep Session abstract view within 200 characters (#3059) on FOSSASIA's Open Event Server project. There is this one way to ellipsis a paragraph in html-css and that is by using the text-overflow property: .div_class{ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }’’ But the downside of this is the one line ellipis. Eg: My name is Medozonuo. I am..... And here you might pretty much want to ellipsis after a few characters in multiple lines, given that your div space is small and you do want to wrap your paragraph. Or maybe not. So jquery to the rescue. There are two ways you can easily do this multiple line ellipsis: 1) Height-Ellipsis (Using the do-while loop): //script: if ($('.div_class').height() > 100) {    var words = $('.div_class').html().split(/\s+/);    words.push('...');    do {        words.splice(-2, 1);        $('.div_class').html( words.join(' ') );    } while($('.div_class').height() > 100); } Here, you check for the div content's height and split the paragraph after that certain height and add a "...", do- while making sure that the paragraphs are in multiple lines and not in one single line. But checkout for that infinite loop. 2) Length-Ellipsis (Using substring function):   //script: $.each($('.div_class'), function() {        if ($(this).html().length > 100) {               var cropped_words = $(this).html();               cropped_words = cropped_words.substring(0, 200) + "...";               $(this).html(cropped_words);        } }); Here, you check for the length/characters rather than the height, take in the substring of the content starting from 0-th character to the 200-th character and then add in extra "...". This is exactly how I used it in the code. $.each($('.short_abstract',function() { if ($(this).html().length > 200) { var words = $(this).html(); words = words.substring(0,200 + "..."; $(this).html(words); } }); So ellipsing paragraphs over heights and lengths can be done using jQuery likewise.

Continue ReadingOpen Event Server: No (no-wrap) Ellipsis using jquery!