Job Opportunity: Fullstack Python Django VueJS Developer with DevOps Expertise

Location: Remote
Type: Full-time or Part-time

Are you passionate about open-source development and enjoy working across the stack and infrastructure? Join FOSSASIA, a global community advancing technology through open-source projects. We’re looking for a skilled Fullstack Developer with DevOps expertise to contribute to the Eventyay platform—an open-source event management system powering ticketing, speaker management, and virtual events.

If you thrive in a collaborative environment and are excited to tackle both development and deployment challenges, we’d love to hear from you.

[Apply Here]


Responsibilities

  • Frontend and Backend Development:

    • Build and maintain responsive user interfaces for Eventyay Tickets, Talk, and Video using Vue.js and Django.

    • Design and implement APIs and backend systems for scalability and reliability.

    • Use AI tools (e.g., GitHub Copilot, ChatGPT) to enhance development efficiency.

    • Collaborate with the team to deliver new features, resolve bugs, and optimize performance.

  • DevOps and Infrastructure Management:

    • Set up and maintain deployment pipelines for efficient and automated CI/CD.

    • Manage cloud infrastructure to ensure high availability and scalability.

    • Implement containerization using Docker.

    • Monitor, troubleshoot, and optimize system performance using tools like Sentry, Prometheus and Grafana.

  • Cross-Team Collaboration:

    • Work closely with team members to integrate and deploy features seamlessly.

    • Ensure compliance with data protection standards such as GDPR.

  • Testing and Documentation:

    • Write tests to maintain code quality and contribute to comprehensive documentation.


Requirements

  • Development Expertise:

    • Strong knowledge of Vue.js, Django, and REST API development.

    • Proficiency in HTML, CSS, and JavaScript.

    • Experience with relational databases like PostgreSQL.

  • AI Tool Proficiency:

    • Ability to use AI tools like GitHub Copilot or ChatGPT to accelerate coding and problem-solving.

  • DevOps Skills:

    • Proficiency with Docker, and CI/CD pipelines.

    • Familiarity with cloud platforms like like Hetzner, and AWS.

    • Experience in Linux system administration and scripting (e.g., Bash, Python).

    • Knowledge of monitoring tools (e.g., Prometheus, Grafana) and security best practices.

  • Collaboration and Open Source:

    • Experience with Git and collaborative workflows.

    • Familiarity with open-source projects and adherence to FOSSASIA Best Practices.


Relevant Repositories


Why Join Us?

  • Competitive Compensation: Receive an attractive remuneration package that reflects your skills and contributions.
  • Global Exposure: Participate in international developer events and conferences, connecting with industry leaders and innovators.
  • Open Source Innovation: Work on impactful, community-driven projects that make a difference.
  • Global Team: Collaborate with a talented, diverse group of individuals worldwide.
  • Work-Life Balance: Enjoy the flexibility of remote work with a schedule that adapts to your needs.
  • Career Growth: Develop hands-on experience in fullstack development, DevOps, and AI tools, setting you apart in the tech industry.

How to Apply

If you’re excited to take on both development and DevOps responsibilities in a dynamic open-source project, submit your application:

Include:

  1. Your CV/Resume.

  2. Brief info explaining your interest in the position.

  3. Links to your GitHub/Portfolio or examples of previous work (if available).


Join FOSSASIA and help shape the future of event management technology with the Eventyay platform and apply!

Continue ReadingJob Opportunity: Fullstack Python Django VueJS Developer with DevOps Expertise

Internship Opportunity: Fullstack Developer with DevOps Expertise

Location: Remote
Type: Internship (Minimum Duration: 3 months)
Organization: FOSSASIA

Are you passionate about open-source development and enjoy working across the stack and infrastructure? Join FOSSASIA, a global community advancing technology through open-source projects. We’re offering an exciting internship opportunity for a Fullstack Developer with DevOps expertise to contribute to the Eventyay platform—an open-source event management system powering ticketing, speaker management, and virtual events.

If you thrive in a collaborative environment and are eager to tackle development challenges independently while leveraging AI tools to speed up workflows, we’d love to hear from you.

Apply Here


Responsibilities

  • Frontend and Backend Development:

    • Build and maintain responsive user interfaces for Eventyay Tickets, Talk, and Video using Vue.js and Django.

    • Implement and refine APIs and backend systems with guidance from senior developers.

    • Use AI tools (e.g., GitHub Copilot, ChatGPT) to enhance development efficiency.

    • Debug and resolve issues independently with minimal supervision.

  • DevOps and Infrastructure Support:

    • Learn to set up and maintain basic deployment pipelines for CI/CD.

    • Assist in managing cloud infrastructure under guidance.

    • Explore and implement containerization using Docker for specific use cases.

    • Monitor system performance and assist in troubleshooting using tools like Sentry and Grafana.

  • Collaboration and Documentation:

    • Work closely with team members to integrate features and resolve issues collaboratively.

    • Document development processes and write clear instructions for future use.


Requirements

  • Development Skills:

    • Basic knowledge of Vue.js, Django, and REST API development.

    • Familiarity with HTML, CSS, and JavaScript.

    • Experience with programming projects during university or personal initiatives.

  • AI Tool Proficiency:

    • Ability to use AI tools like GitHub Copilot or ChatGPT to accelerate coding and problem-solving.

  • DevOps Fundamentals:

    • Exposure to Docker and basic CI/CD workflows.

    • Understanding of cloud platforms like Hetzner or AWS (hands-on experience is a plus).

    • Comfort with Linux command-line basics and scripting (e.g., Bash, Python).

  • Collaboration and Initiative:

    • Ability to work independently while seeking guidance when needed.

    • Familiarity with Git and version control workflows.

    • Interest in open-source contributions and adherence to FOSSASIA Best Practices.


Relevant Repositories


Why Join Us?

  • Competitive Compensation: Receive an attractive remuneration package that reflects your skills and contributions.
  • Global Exposure: Participate in international developer events and conferences, connecting with industry leaders and innovators.
  • Open Source Innovation: Work on impactful, community-driven projects that make a difference.
  • Global Team: Collaborate with a talented, diverse group of individuals worldwide.
  • Work-Life Balance: Enjoy the flexibility of remote work with a schedule that adapts to your needs.
  • Career Growth: Develop hands-on experience in fullstack development, DevOps, and AI tools, setting you apart in the tech industry.

How to Apply

If you’re excited to take on both development and DevOps responsibilities in a dynamic open-source project, submit your application via this form:

Include:

  1. Your CV/Resume.

  2. Brief info explaining your interest in the internship.

  3. Links to your GitHub/Portfolio or examples of previous work (if available).


Join FOSSASIA as an intern and help shape the future of event management technology with the Eventyay platform!

Continue ReadingInternship Opportunity: Fullstack Developer with DevOps Expertise

Python / JS Full Stack Developer for Eventyay Project

Location: Remote
Type: Job or Contract (Minimum Duration: 3 months)
Organization: FOSSASIA

Are you passionate about open-source development and enjoy working across the stack and infrastructure? Join FOSSASIA, a global community advancing technology through open-source projects. We’re offering an exciting internship opportunity for a Fullstack Developer with DevOps expertise to contribute to the Eventyay platform—an open-source event management system powering ticketing, speaker management, and virtual events.

If you thrive in a collaborative environment and are eager to tackle development challenges independently while leveraging AI tools to speed up workflows, we’d love to hear from you.

Apply Here


Responsibilities

  • Frontend and Backend Development:

    • Build and maintain responsive user interfaces for Eventyay Tickets, Talk, and Video using Vue.js and Django.

    • Implement and refine APIs and backend systems with guidance from senior developers.

    • Use AI tools (e.g., GitHub Copilot, ChatGPT) to enhance development efficiency.

    • Debug and resolve issues independently with minimal supervision.

  • DevOps and Infrastructure Support:

    • Learn to set up and maintain basic deployment pipelines for CI/CD.

    • Assist in managing cloud infrastructure under guidance.

    • Explore and implement containerization using Docker for specific use cases.

    • Monitor system performance and assist in troubleshooting using tools like Sentry and Grafana.

  • Collaboration and Documentation:

    • Work closely with team members to integrate features and resolve issues collaboratively.

    • Document development processes and write clear instructions for future use.


Requirements

  • Development Skills:

    • Basic knowledge of Vue.js, Django, and REST API development.

    • Familiarity with HTML, CSS, and JavaScript.

    • Experience with programming projects during university or personal initiatives.

  • AI Tool Proficiency:

    • Ability to use AI tools like GitHub Copilot or ChatGPT to accelerate coding and problem-solving.

  • DevOps Fundamentals:

    • Exposure to Docker and basic CI/CD workflows.

    • Understanding of cloud platforms like Hetzner or AWS (hands-on experience is a plus).

    • Comfort with Linux command-line basics and scripting (e.g., Bash, Python).

  • Collaboration and Initiative:

    • Ability to work independently while seeking guidance when needed.

    • Familiarity with Git and version control workflows.

    • Interest in open-source contributions and adherence to FOSSASIA Best Practices.


Relevant Repositories


Why Join Us?

  • Attractive Internship Compensation: Receive an attractive package that reflects your skills and contributions.
  • Open Source Innovation: Work on impactful, community-driven projects that make a difference.
  • Global Team: Collaborate with a talented, diverse group of individuals worldwide.
  • Flexible Work Environment: Enjoy remote work and a flexible schedule.
  • Skill Development: Gain hands-on experience in fullstack development and DevOps while leveraging AI tools.

How to Apply

If you’re excited to take on both development and DevOps responsibilities in a dynamic open-source project, submit your application via this form:

Include:

  1. Your CV/Resume.

  2. Brief info explaining your interest in the internship.

  3. Links to your GitHub/Portfolio or examples of previous work (if available).


Join FOSSASIA as an intern and help shape the future of event management technology with the Eventyay platform!

Continue ReadingPython / JS Full Stack Developer for Eventyay Project

Reducing the YouTube response time by 90%

In this blog post, we are going to cover how the audio from Youtube is being used in SUSI Smart Speaker and how we reduced the response time from ~40 seconds to ~4 seconds for an average music video length.

First Approach

Earlier, we were using MPV player’s inbuilt feature to fetch the YouTube music. However, MPV player was a bulky option and the music server had to be started every time before initiating a music video.

video_process = subprocess.Popen([‘mpv’, ‘–no-video’, ‘https://www.youtube.com/watch?v=’ + video_url[4:], ‘–really-quiet’]) # nosec #pylint-disable type: ignore requests.get(‘http://localhost:7070/song/’ + video_url) self.video_process = video_process stopAction.run() stopAction.detector.terminate()

Making it Efficient

To reduce the response time, we created a custom Music Server based on Flask,python-vlc and python-pafy which accepts requests from the main client and instructs the System to play the music with just 90% more efficiency.

app = Flask(__name__)

Instance = vlc.Instance(‘–no-video’)

player = Instance.media_player_new()

url = @app.route(‘/song’, methods=[‘GET’])

def youtube():

    vid = request.args.get(‘vid’)

    url = ‘https://www.youtube.com/watch?v=’ + vid

    video = pafy.new(url)
    streams = video.audiostreams 

    best = streams[3]

    playurl = best.url

    Media = Instance.media_new(playurl)

    Media.get_mrl()

    player.set_media(Media)

    player.play()

    display_message = {“song”:“started”}

    resp = jsonify(display_message)

    resp.status_code = 200

    return resp

However, shifting to this Server removed the ability to process multiple queries and hence we were unable to pause/play/stop the music until it completed the time duration. We wanted to retain the ability to have ‘play/pause/stop’ actions without implementing multiprocessing or multithreading as it would’ve required extensive testing to successfully implement them without creating deadlocks and would’ve been overkill for a simple feature.

Bringing Back the Lost Functionalities

The first Step we took was to remove the vlc-python module and implement a way to obtain an URL that we use in another asynchronous music player.

@app.route(‘/song’, methods=[‘GET’])
def youtube():

    vid = request.args.get(‘vid’)

    streams = video.audiostreams

    best = streams[3]

    playurl = best.url 

    display_message = {“song”: “started”, “url”: playurl}

    resp = jsonify(display_message)

    resp.status_code = 200

    return resp

The next issue was to actually find a way to run the Music Player asynchronously. We used the `subprocess. Popen` method and cvlc to play the songs asynchronously.

try:

    x = requests.get(‘http://localhost:7070/song?vid=’ + video_url[4:])

    data = x.json()

    url = data[‘url’]

    video_process = subprocess.Popen([‘cvlc’, ‘http’ + url[5:], ‘–no-video’])

    self.video_process = video_process

except Exception as e:

    logger.error(e);

And this is how we were able to increase the efficiency of the music player while maintaining the functionalities.

References

Continue ReadingReducing the YouTube response time by 90%

Using a Flask Server to Connect to SUSI smart speaker

A smart speaker becomes significantly smarter when it is connected to a Smart-Phone.

So, we added a way to connect the Smart-Phone to the Smart Speaker and initiate the first way towards a Smart Home.

Use a simple HTTP connection protocol and deploy a light-weight server on the Raspberry Pi to allow connection from a mobile phone.

Step 1: Setting Up the server

Use flask to deploy a light-weight server on the raspberry pi. We’ll install flask using raspbian repos.

 

1>Install Flask by using the following command

sudo apt-get install python3-flask

 

2> Setting up the boilerplate code.

Open the terminal and type the following commands

`

mkdir server_app
cd server_app

touch app.py

`

 

Add the following code to your app.py file. This create a server at localhost:5000

 

from flask import Flask

app = Flask(__name__)

@app.route(‘/’)
def index():
   return ‘Hello world’

if __name__ == ‘__main__’:
   app.run(debug=False, host=‘0.0.0.0’)  #This will allow the server to be accessible on all devices

 

Step 2: Adding Endpoints

Now , add endpoints which will trigger the scripts during initialisation of the raspberry Pi. This will trigger the respective endpoints

@app.route(‘/auth/<auth>/<email>/<passwd>’)
def login(auth, email, passwd):
os.system(‘sudo ./login.sh {} {} {}’.format(auth, email,passwd)) #nosec #pylint-disable type: ignore
return ‘Authenticated’ # pylint-enable@app.route(‘/wifi_credentials/<wifissid>/<wifipassd>’)
def wifi_config(wifissid,wifipassd):
wifi_ssid = wifissid
wifi_password = wifipassd
os.system(‘sudo ./home/pi/SUSI.AI/susi_linux/access_point/wifi_search.sh {} {}’.format(wifi_ssid,wifi_password))  #nosec #pylint-disable type: ignore
return ‘Wifi Configured’ # pylint-enable

 

Step 3: Connecting to the endpoints

Now, try and hit the API endpoints to get the response.

eg.As shown in the above example, you will be getting a single line response and will execute a bash script behind the scenes

Now you can access the other endpoints and configure the clients with the SUSI Smart Speaker

References

 

Tags

fossasia,GSoC,Python, Flask , raspberryPi, SUSI,smart-speaker,FOSSASIA

Continue ReadingUsing a Flask Server to Connect to SUSI smart speaker

Adding device names’ support for check-ins to Open Event Server

The Open Event Server provides backend support to Open Event Organizer Android App which is used to check-in attendees in an event. When checking in attendees, it is important for any event organizer to keep track of the device that was used to check someone in. For this, we provide an option in the Organizer App settings to set the device name. But this device name should have support in the server as well.

The problem is to be able to add device name data corresponding to each check-in time. Currently attendees model has an attribute called `checkin-times`, which is a csv of time strings. For each value in the csv, there has to be a corresponding device name value. This could be achieved by providing a similar csv key-value pair for “device-name-checkin”.

The constraints that we need to check for while handling device names are as follows:

  • If there’s `device_name_checkin` in the request, there must be `is_checked_in` and `checkin_times` in the data as well.
  • Number of items in checkin_times csv in data should be equal to the length of the device_name_checkin csv.
  • If there’s checkin_times in data, and device-name-checkin is absent, it must be set to `-` indicating no set device name.
if ‘device_name_checkin’ in data and data[‘device_name_checkin’] is not None:
  if ‘is_checked_in’ not in data or not data[‘is_checked_in’]:
       raise UnprocessableEntity(
           {‘pointer’: ‘/data/attributes/device_name_checkin’},
           “Attendee needs to be checked in first”
       )
  elif ‘checkin_times’ not in data or data[‘checkin_times’] is None:
      raise UnprocessableEntity(
          {‘pointer’: ‘/data/attributes/device_name_checkin’},
           “Check in Times missing”
      )
  elif len(data[‘checkin_times’].split(“,”)) != len(data[‘device_name_checkin’].split(“,”)):
     raise UnprocessableEntity(
           {‘pointer’: ‘/data/attributes/device_name_checkin’},
           “Check in Times missing for the corresponding device name”
         )
 if ‘checkin_times’ in data:
   if ‘device_name_checkin’ not in data or data[‘device_name_checkin’] is None:
       data[‘device_name_checkin’] = ‘-‘

The case is a little different for a PATCH request since we need to check for the number of items differently like this:

if ‘device_name_checkin’ in data and data[‘device_name_checkin’] is not None:
            if obj.device_name_checkin is not None:
               data[‘device_name_checkin’] = ‘{},{}’.format(obj.device_name_checkin, data[‘device_name_checkin’])                                                   
            if len(data[‘checkin_times’].split(“,”)) != len(data[‘device_name_checkin’].split(“,”)):
               raise UnprocessableEntity(
                   {‘pointer’: ‘/data/attributes/device_name_checkin’},
                   “Check in Time missing for the corresponding device name”)

Since we expect only the latest value to be present in a PATCH request, we first add it to the object by formatting using:

'{},{}'.format(obj.device_name_checkin, data['device_name_checkin'])

and then compare the length of the obtained CSVs for check in times and device names, so that corresponding to each check in time, we have either a device name or the default fill in value ‘-’.

That’s all. Read the full code here.

Requests and Responses:

Resources

  1. SQLAlchemy Docs
    https://docs.sqlalchemy.org/en/latest/
  2. Alembic Docs
    http://alembic.zzzcomputing.com/en/latest/
  3. Flask REST JSON API Classical CRUD operation
    https://flask-rest-jsonapi.readthedocs.io/en/latest/quickstart.html#classical-crud-operations
Continue ReadingAdding device names’ support for check-ins to Open Event Server

Modifying Allowed Usage for a User

Badgeyay has been progressing in a very good pace. There are a lot of features being developed and modified in this project. One such feature that has been added is the increasing allowed usage of a user by an admin.

What is Allowed Usage?

Allowed usage is an integer associated with a particular user that determines the number of badges that a person can generate using a single email id. This will allow us to keep track of the number of badges being produced by a particular ID and all.

Modifying the Allowed Usage

This feature is basically an Admin feature, that will allow an admin to increase or decrease the allowed usage of a particular user. This will ensure that if incase a particular user has his/her usage finished, then by contacting the admin, he/she can get the usage refilled.

Adding the functionality

The functionality required us to to add two things

  • A schema for modifying allowed user
  • A route in backend to carry out the functionality

So, Let us start by creating the schema

class UserAllowedUsage(Schema):
class Meta:
type_ =
‘user_allowed_usage’
kwargs = {
‘id’: ‘<id>’}

id = fields.Str(required=True, dump_only=True)
allowed_usage = fields.Str(required=
True, dump_only=True)

Once we have our schema created, then we can create a route to modify the allowed usage for a particular user.

This route will be made accessible to the admin of Badgeyay.

@router.route(‘/add_usage’, methods=[‘POST’])
def admin_add_usage():
try:
data = request.get_json()[
‘data’]
print(data)
except Exception:
return ErrorResponse(JsonNotFound().message, 422, {‘Content-Type’: ‘application/json’}).respond()

uid = data[‘uid’]
allowed_usage = data[
‘allowed_usage’]
user = User.getUser(user_id=uid)
user.allowed_usage = user.allowed_usage + allowed_usage
db.session.commit()

return jsonify(UserAllowedUsage().dump(user).data)

The add_usage route is given above. We can use this route to increase the usage of a particular user.

Given below is an image that shows the API working.

Resources

Continue ReadingModifying Allowed Usage for a User

Open Event Server – Export Speakers as PDF File

FOSSASIA‘s Open Event Server is the REST API backend for the event management platform, Open Event. Here, the event organizers can create their events, add tickets for it and manage all aspects from the schedule to the speakers. Also, once he/she makes his event public, others can view it and buy tickets if interested.

The organizer can see all the speakers in a very detailed view in the event management dashboard. He can see the statuses of all the speakers. The possible statuses are pending, accepted, and rejected. He/she can take actions such as editing the speakers.

If the organizer wants to download the list of all the speakers as a PDF file, he or she can do it very easily by simply clicking on the Export As PDF button in the top right-hand corner.

Let us see how this is done on the server.

Server side – generating the Speakers PDF file

Here we will be using the pisa package which is used to convert from HTML to PDF. It is a html2pdf converter which uses ReportLab Toolkit, the HTML5lib and pyPdf. It supports HTML5 and CSS 2.1 (and some of CSS 3). It is completely written in pure Python so it is platform independent.

from xhtml2pdf import pisa<

We have a utility method create_save_pdf which creates and saves PDFs from HTML. It takes the following arguments:

  • pdf_data – This contains the HTML template which has to be converted to PDF.
  • key – This contains the file name
  • dir_path – This contains the directory

It returns the newly formed PDF file. The code is as follows:

def create_save_pdf(pdf_data, key, dir_path='/static/uploads/pdf/temp/'):
   filedir = current_app.config.get('BASE_DIR') + dir_path

   if not os.path.isdir(filedir):
       os.makedirs(filedir)

   filename = get_file_name() + '.pdf'
   dest = filedir + filename

   file = open(dest, "wb")
   pisa.CreatePDF(io.BytesIO(pdf_data.encode('utf-8')), file)
   file.close()

   uploaded_file = UploadedFile(dest, filename)
   upload_path = key.format(identifier=get_file_name())
   new_file = upload(uploaded_file, upload_path)
   # Removing old file created
   os.remove(dest)

   return new_file

The HTML file is formed using the render_template method of flask. This method takes the HTML template and its required variables as the arguments. In our case, we pass in ‘pdf/speakers_pdf.html’(template) and speakers. Here, speakers is the list of speakers to be included in the PDF file. In the template, we loop through each item of speakers. We print his name, email, list of its sessions, mobile, a short biography, organization, and position. All these fields form a row in the table. Hence, each speaker is a row in our PDF file.

The various columns are as follows:

<thead>
<tr>
   <th>
       {{ ("Name") }}
   </th>
   <th>
       {{ ("Email") }}
   </th>
   <th>
       {{ ("Sessions") }}
   </th>
   <th>
       {{ ("Mobile") }}
   </th>
   <th>
       {{ ("Short Biography") }}
   </th>
   <th>
       {{ ("Organisation") }}
   </th>
   <th>
       {{ ("Position") }}
   </th>
</tr>
</thead>

A snippet of the code which handles iterating over the speakers’ list and forming a row is as follows:

{% for speaker in speakers %}
   <tr class="padded" style="text-align:center; margin-top: 5px">
       <td>
           {% if speaker.name %}
               {{ speaker.name }}
           {% else %}
               {{ "-" }}
           {% endif %}
       </td>
       <td>
           {% if speaker.email %}
               {{ speaker.email }}
           {% else %}
               {{ "-" }}
           {% endif %}
       </td>
       <td>
           {% if speaker.sessions %}
               {% for session in speaker.sessions %}
                   {{ session.name }}<br>
               {% endfor %}
           {% else %}
               {{ "-" }}
           {% endif %}
       </td>
      …. So on
   </tr>
{% endfor %}

The full template can be found here.

Obtaining the Speakers PDF file:

Firstly, we have an API endpoint which starts the task on the server.

GET - /v1/events/{event_identifier}/export/speakers/pdf

Here, event_identifier is the unique ID of the event. This endpoint starts a celery task on the server to export the speakers of the event as a PDF file. It returns the URL of the task to get the status of the export task. A sample response is as follows:

{
  "task_url": "/v1/tasks/b7ca7088-876e-4c29-a0ee-b8029a64849a"
}

The user can go to the above-returned URL and check the status of his/her Celery task. If the task completed successfully he/she will get the download URL. The endpoint to check the status of the task is:

and the corresponding response from the server –

{
  "result": {
    "download_url": "/v1/events/1/exports/http://localhost/static/media/exports/1/zip/OGpMM0w2RH/event1.zip"
  },
  "state": "SUCCESS"
}

The file can be downloaded from the above-mentioned URL.

Resources

Continue ReadingOpen Event Server – Export Speakers as PDF File

Open Event Server – Export Sessions as PDF File

FOSSASIA‘s Open Event Server is the REST API backend for the event management platform, Open Event. Here, the event organizers can create their events, add tickets for it and manage all aspects from the schedule to the speakers. Also, once he/she makes his event public, others can view it and buy tickets if interested.

The organizer can see all the sessions in a very detailed view in the event management dashboard. He can see the statuses of all the sessions. The possible statuses are pending, accepted, confirmed and rejected. He/she can take actions such as accepting/rejecting the sessions.

If the organizer wants to download the list of all the sessions as a PDF file, he or she can do it very easily by simply clicking on the Export As PDF button in the top right-hand corner.

Let us see how this is done on the server.

Server side – generating the Sessions PDF file

Here we will be using the pisa package which is used to convert from HTML to PDF. It is a html2pdf converter which uses ReportLab Toolkit, the HTML5lib and pyPdf. It supports HTML5 and CSS 2.1 (and some of CSS 3). It is completely written in pure Python so it is platform independent.

from xhtml2pdf import pisa

We have a utility method create_save_pdf which creates and saves PDFs from HTML. It takes the following arguments:

  • pdf_data – This contains the HTML template which has to be converted to PDF.
  • key – This contains the file name
  • dir_path – This contains the directory

It returns the newly formed PDF file. The code is as follows:

def create_save_pdf(pdf_data, key, dir_path='/static/uploads/pdf/temp/'):
   filedir = current_app.config.get('BASE_DIR') + dir_path

   if not os.path.isdir(filedir):
       os.makedirs(filedir)

   filename = get_file_name() + '.pdf'
   dest = filedir + filename

   file = open(dest, "wb")
   pisa.CreatePDF(io.BytesIO(pdf_data.encode('utf-8')), file)
   file.close()

   uploaded_file = UploadedFile(dest, filename)
   upload_path = key.format(identifier=get_file_name())
   new_file = upload(uploaded_file, upload_path)
   # Removing old file created
   os.remove(dest)

   return new_file

The HTML file is formed using the render_template method of flask. This method takes the HTML template and its required variables as the arguments. In our case, we pass in ‘pdf/sessions_pdf.html’(template) and sessions. Here, sessions is the list of sessions to be included in the PDF file. In the template, we loop through each item of sessions and check if it is deleted or not. If it not deleted then we print its title, state, list of its speakers, track, created at and has an email been sent or not. All these fields form a row in the table. Hence, each session is a row in our PDF file.

The various columns are as follows:

<thead>
<tr>
   <th>
       {{ ("Title") }}
   </th>
   <th>
       {{ ("State") }}
   </th>
   <th>
       {{ ("Speakers") }}
   </th>
   <th>
       {{ ("Track") }}
   </th>
   <th>
       {{ ("Created At") }}
   </th>
   <th>
       {{ ("Email Sent") }}
   </th>
</tr>
</thead>

A snippet of the code which handles iterating over the sessions list and forming a row is as follows:

{% for session in sessions %}
   {% if not session.deleted_at %}
       <tr class="padded" style="text-align:center; margin-top: 5px">
           <td>
               {% if session.title %}
                   {{ session.title }}
               {% else %}
                   {{ "-" }}
               {% endif %}
           </td>
           <td>
               {% if session.state %}
                   {{ session.state }}
               {% else %}
                   {{ "-" }}
               {% endif %}
           </td>
           <td>
               {% if session.speakers %}
                   {% for speaker in session.speakers %}
                       {{ speaker.name }}<br>
                   {% endfor %}
               {% else %}
                   {{ "-" }}
               {% endif %}
           </td>
          ….. And so on
       </tr>
   {% endif %}
{% endfor %}

The full template can be found here.

Obtaining the Sessions PDF file:

Firstly, we have an API endpoint which starts the task on the server.

GET - /v1/events/{event_identifier}/export/sessions/pdf

Here, event_identifier is the unique ID of the event. This endpoint starts a celery task on the server to export the sessions of the event as a PDF file. It returns the URL of the task to get the status of the export task. A sample response is as follows:

{
  "task_url": "/v1/tasks/b7ca7088-876e-4c29-a0ee-b8029a64849a"
}

The user can go to the above-returned URL and check the status of his/her Celery task. If the task completed successfully he/she will get the download URL. The endpoint to check the status of the task is:

and the corresponding response from the server –

{
  "result": {
    "download_url": "/v1/events/1/exports/http://localhost/static/media/exports/1/zip/OGpMM0w2RH/event1.zip"
  },
  "state": "SUCCESS"
}

The file can be downloaded from the above-mentioned URL.

Resources

Continue ReadingOpen Event Server – Export Sessions as PDF File

Upgrading Open Event to Use Sendgrid API v3

Sendgrid recently upgraded their web API to send emails, and support for previous versions was deprecated. As a result, Open Event Server’s mail sending tasks were rendered unsuccessful, because the requests they were sending to Sendgrid were not being processed. On top of that, it was also found out later that the existing Sendgrid API key on the development server was expired. This had to be fixed at the earliest because emails are a core part of Open Event functionality.

The existing way for emails to be sent via Sendgrid used to hit the endpoint “https://api.sendgrid.com/api/mail.send.json” to send emails. Also, the payload structure was as follows:

payload = {
    'to': to,
    'from': email_from,
    'subject': subject,
    'html': html
}

Also, a header  “Authorization”: “Bearer ” accompanied the above payload. However, Sendgrid changed the payload structure to be of the following format:

{

“personalizations”: [

{“to”: [

{“email”: “example@example.com“}

]

}

],

“from”: {

“email”: “example@example.com

},

“subject”: “Hello, World!”,

“content”: [

{

“type”: “text/plain”,

“value”: “Heya!”

}

]

}

Furthermore, the endpoint was changed to be “https://api.sendgrid.com/v3/mail/send”. To incorporate all these changes with the minimum number of modified lines in the codebase, it was required for that the structure change itself happens at a fairly low level. This was because there are lots of features in the server that perform a wide variety of email actions. Thus, it was clear that changing all of them will not be the most efficient thing to do. So the perfect place to implement the API changes was the function send_email() in mail.py, because all other higher-level email functions are built on top of this function. But this was not the only change, because this function itself used another function, called send_email_task() in tasks.py, specifically for sending email via Sendgrid. So, in conclusion, the header modifications were made in send_email() and payload structure as well as endpoint modifications were made within send_email_task(). This brought the server codebase back on track to send emails successfully. Finally, the key for development server was also renewed and added to its settings in the Heroku Postgres database.

Screenshots:

Screen Shot 2018-08-21 at 3.40.12 PM.png

Screen Shot 2018-08-21 at 3.40.32 PM.png

Resources

Continue ReadingUpgrading Open Event to Use Sendgrid API v3