Job Opportunity: Fullstack Python Django VueJS Developer with DevOps Expertise

Location: RemoteType: 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 Eventyay Tickets Eventyay Talk Eventyay Video 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: Your CV/Resume. Brief info explaining your interest in the position. 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

How does SUSI AI web bot plugin work

   In this blog, we’ll learn how SUSI AI web plugin works. Normally, for any bot like Kik bot, we don’t have to worry about the chat boxes or the way chatbot is rendered on screen because all of that is handled by the service on which we are building our bot. So, for these bots, we simply take the text input from user, send a GET request to SUSI server, receive the response and display it. This is not the case with SUSI AI Web Bot plugin. In this case, there is not Bot platform. Hence, there are a lot of other things that we need to take care of here. Let’s explore the main ones among them. Adding bot to website: The final product that we’re going to provide to the client is going to be just a JavaScript code. Hence the HTML code of bot widget needs to be added to the <body> of the website. This is done usingappend() method of jQuery. The html() method sets or returns the content (innerHTML) of selected elements. Syntax: $(selector).append(content) So we store the HTML code of bot widget in a variable and then returns the content of that variable to the body tag using: $("body").append(mybot); Here “mybot” is the variable containing HTML code of bot widget. The text boxes of user texts and bot texts are added to a HTML element in the same way. Event handling: JavaScript's interaction with HTML is handled through events that occur when the user or the browser manipulates a page. Loading of the page, clicking, pressing a key, closing a window, resizing the window etc are all examples of events. In the SUSI AI web bot, there are two major events. Clicking a button This is required for allowing clicks on send button. Clicking of a button can be done in many ways. There are two major ways that are used in SUSI AI web bot. Some elements already exist on the webpage. For example - the HTML code of web bot. It is added to the body tag as soon as webpage loads. For these elements we use click(). The click() binding is called “direct” binding which can be attached to the elements that already exists. Syntax: $(selector).click(function); Selector - Class, ID or name of HTML element. Function - Specifies the function to run when click event occurs. Some elements are dynamically generated. This means that they are added at a later point of time. For example - the text messages are added when user types in. This happens after loading of page. For this, we have to create “delegated” binding by using on() method. Syntax: $(selector).on(event,childSelector,data,function); Selector - Class, ID or name of HTML element Event - (Required) Specifies one or more event(s) or namespaces to attach to the selected elements. childSelector - (Optional) Specifies that the event handler should only be attached to the specified child elements Data - (Optional) Specifies additional data to pass along to the function Function - Specifies the function to run when click event occurs. Pressing the enter key For identifying which key…

Continue ReadingHow does SUSI AI web bot plugin work

Build Button Resolution in Meilix

Meilix Generator is a webapp which has a build button and after clicking the button it triggers the travis of Meilix repository. You can even generate ISO from your phone. But while opening the web app on a phone, we came to see that the build button is not properly visible. Footer of the page hide the build button. This blog shows the way to identify the build button issue and to make it responsive for all screen sizes. Error Rectification There are two elements that need correction: Build button Footer Build Button <input class="btn btn-info mx-auto btn-block" id="file-upload" value="Build" required="" type="submit">   Here build button act as a input submit button. class="form-group"> class="btn btn-info mx-auto btn-block" id="file-upload" value="Build" required="" type="submit" style="height: 50px; width: 360px; border-radius: 500px;"/>   We first added the group to include the button in that form. Then we add the build button property to give the button certain look. But now also the footer overlap the button. So now we need to work on footer part. Footer <footer class="footer">   We remove footer class and made a new div tag with the id = “deployment”. Then we set the css for the deployment id to customise its appearance. #deployment { position: absolute; text-align: center; width: 100%; padding: 10px; bottom: 0px; border-top: 1px solid #bfbfbf; background-color: #f9f9f9; }   This set the footer and build button appropriate to become responsive for all sizes. Reference: HTML form tag Input type Submit tag

Continue ReadingBuild Button Resolution in Meilix

Implementing Direct URL in loklak Media Wall

Direct URL is a web address which redirects the user to the preset customized media wall so that the media wall can directly be used to be displayed on the screen. Loklak media wall provides direct URL which has information related to customizations set by the user included in the web address. These customizations, as the query parameters are detected when the page is initialized and actions are dispatched to make changes in the state properties, and hence, the UI properties and the expected behaviour of media wall. In this blog, I would be explaining how I implemented direct URL in loklak media wall and how customizations are detected to build on initialization of component, a customized media wall. Flow Chart Working Media Wall Direct URL effect This effect detects when the WALL_GENERATE_DIRECT_URL action is dispatched and creates a direct URL string from all the customization state properties and dispatches a side action WallShortenDirectUrlAction() and stores direct URL string as a state property. For this, we need to get individual wall customization state properties and create an object for it and supply it as a parameter to the generateDirectUrl() function. Direct URL string is returned from the function and now, the action is dispatched to store this string as a state property. @Effect() generateDirectUrl$: Observable<Action> = this.actions$ .ofType(mediaWallDirectUrlAction.ActionTypes.WALL_GENERATE_DIRECT_URL) .withLatestFrom(this.store$) .map(([action, state]) => { return { query: state.mediaWallQuery.query, . . . wallBackground: state.mediaWallCustom.wallBackground }; }) .map(queryObject => { const configSet = { queryString: queryObject.query.displayString, . . . wallBackgroundColor: queryObject.wallBackground.backgroundColor } const shortenedUrl = generateDirectUrl(configSet); return new mediaWallDirectUrlAction.WallShortenDirectUrlAction(shortenedUrl); }); Generate Direct URL function This function generates Direct URL string from all the current customization options value. Now,  keys of the object are separated out and for each element of the object, it checks if there is some current value for the elements and it then first parses the value of the element into URI format and then, adds it to the direct URL string. In such a way, we are creating a direct URL string with these customizations provided as the query parameters. export function generateDirectUrl(customization: any): string { const shortenedUrl = '';const activeFilterArray: string[] = new Array<string>(); let qs = ''; Object.keys(customization).forEach(config => { if (customization[config] !== undefined && customization[config] !== null) { if (config !== 'blockedUser' && config !== 'hiddenFeedId') { qs += `${config}=${encodeURIComponent(customization[config])}&`; } else { if (customization[config].length > 0) { qs += `${config}= ${encodeURIComponent(customization[config].join(','))}&`; } } } }); qs += `ref=share`; return qs; } Creating a customized media wall Whenever the user searches for the URL link on the web, a customized media wall must be created on initialization. The media wall component detects and subscribes to the URL query parameters using the queryParams API of the ActivatedRoute. Now, the values are parsed to a required format of payload and the respective actions are dispatched according to the value of the parameters. Now, when all the actions are dispatched, state properties changes accordingly. This creates a unidirectional flow of the state properties from the URL parameters to the…

Continue ReadingImplementing Direct URL in loklak Media Wall

Generating Ticket PDFs in Open Event API Server

In the ordering system of Open Event API Server, there is a requirement to send email notifications to the attendees. These attendees receive the URL of the pdf of the generated ticket. On creating the order, first the pdfs are generated and stored in the preferred storage location and then these are sent to the users through the email. Generating PDF is a simple process, using xhtml2pdf we can generate PDFs from the html. The generated pdf is then passed to storage helpers to store it in the desired location and pdf-url is updated in the attendees record. Sample PDF PDF Template The templates are written in HTML which is then converted using the module xhtml2pdf. To store the templates a new directory was created at  app/templates where all HTML files are stored. Now, The template directory needs to be updated at flask initializing app so that template engine can pick the templates from there. So in app/__init__.py we updated flask initialization with template_dir = os.path.dirname(__file__) + "/templates" app = Flask(__name__, static_folder=static_dir, template_folder=template_dir) This allows the template engine to pick the templates files from this template directory. Generating PDFs Generating PDF is done by rendering the html template first. This html content is then parsed into the pdf file = open(dest, "wb") pisa.CreatePDF(cStringIO.StringIO(pdf_data.encode('utf-8')), file) file.close() The generated pdf is stored in the temporary location and then passed to storage helper to upload it. uploaded_file = UploadedFile(dest, filename) upload_path = UPLOAD_PATHS['pdf']['ticket_attendee'].format(identifier=get_file_name()) new_file = upload(uploaded_file, upload_path) This generated pdf path is returned here Rendering HTML and storing PDF for holder in order.ticket_holders:   if holder.id != current_user.id:       pdf = create_save_pdf(render_template('/pdf/ticket_attendee.html', order=order, holder=holder))   else:       pdf = create_save_pdf(render_template('/pdf/ticket_purchaser.html', order=order))   holder.pdf_url = pdf   save_to_db(holder) The html is rendered using flask template engine and passed to create_save_pdf and link is updated on the attendee record. Sending PDF on email These pdfs are sent as a link to the email after creating the order. Thus a ticket is sent to each attendee and a summarized order details with attendees to the purchased. send_email(   to=holder.email,   action=TICKET_PURCHASED_ATTENDEE,   subject=MAILS[TICKET_PURCHASED_ATTENDEE]['subject'].format(       event_name=order.event.name,       invoice_id=order.invoice_number   ),   html= MAILS[TICKET_PURCHASED_ATTENDEE]['message'].format(       pdf_url=holder.pdf_url,       event_name=order.event.name   ) ) References Readme - xhtml2pdf https://github.com/xhtml2pdf/xhtml2pdf/blob/master/README.rst Using xhtml2pdf and create pdfs https://micropyramid.com/blog/generating-pdf-files-in-python-using-xhtml2pdf/  

Continue ReadingGenerating Ticket PDFs in Open Event API Server

Creating an Infobox for Mobile View Using Angular in Susper

In Susper, the Information and Analytics boxes disappeared for widths smaller than 1100px, since they were too big to fit in the existing page layout. In Laptop view: In Mobile view: Hence we decided to design a new Info-cum-Analytics box for mobile devices, where the Analytics are displayed only if the ‘Show Analytics’ button is clicked and they are hidden on clicking the ‘Hide Analytics’ button. The following is the html code for the Infobox: <div class="combo-box"> <app-infobox></app-infobox> <button class="btn" id="toggle-button" (click)="BoxToggle()" type="button" data-toggle="collapse" data-target="#statbox" aria-expanded="false" aria-controls="collapseExample"> {{boxMessage}} Analytics </button> <app-statsbox class="collapse" id="statbox"></app-statsbox> </div> We can make the following observations: The combo box has both the Info and the Stats box, in addition to a button, which toggles the display status of the Analytics box. To display the appropriate message on the button, we have a special function called BoxToggle() which sets the value of boxMessage() The data-toggle attribute of the button has been set to collapse and the data-target is statbox The statsbox has the class collapse, so that it collapses on clicking on the Toggle button. The typescript code is as follows: BoxToggle() { if (this.boxMessage === 'Show') { this.boxMessage = 'Hide'; } else { this.boxMessage = 'Show'; } } This code thus effectively toggles between the messages Show and Hide, if the button is clicked when the message says Show, it changes to Hide and vice-versa The CSS code: @media screen and (min-width:768px) { div.combo-box { display: none; } } The css code ensures that this combo-box is displayed only for widths less than 768px (only mobile and tablet screens). To view the entire code you can check results.component.html and results.component.css in the Susper repository. References W3 Schools for Collapsible buttons and their implementation: https://www.w3schools.com/bootstrap/bootstrap_collapse.asp Official Bootstrap documentation for collapsible buttons: https://v4-alpha.getbootstrap.com/components/collapse/

Continue ReadingCreating an Infobox for Mobile View Using Angular in Susper

How to Parse HTML Tags and Anchor Clickable Links in SUSI Android App

Html tags are used to define how contents of a webpage should be formatted and displayed. Sometimes the SUSI answer type response contains some html tags but showing these html tags without parsing would distort the normal text flow in SUSI Android. For the query ‘Ask me something’ SUSI’s reply is "data": [      {       "question": "Which soccer team won the Copa Am&eacute;rica 2015 Championship ? ",                                                }] In SUSI Android this message looks like As you can see that showing html tags without parsing distort the normal text flow. So we need to parse html tags properly. We use Html class for this purpose. Html class is present in android.text package and you can import it in the class where you want to use it. import android.text.Html fromHtml method of Html class is used to parse html tags. But for API level less than 24 and equal to or greater than 24 we use different parameters in fromHtml method. For API level less than 24 we used Html.fromHtml(model.getContent()) But for API level equal to or greater than 24 we have to use Html.fromHtml(model.getContent(), Html.FROM_HTML_MODE_COMPACT) Here the second parameter is legacy flags which decides how text inside a tag will be shown after parsing. In case of Html.fromHtml(String string) legacy flag is by default FROM_HTML_MODE_LEGACY. It indicates that separate block-level elements with blank lines. So after parsing html tags using fromHtml But return type of fromHtml method is Spanned so if you need String then you have to convert it into string using toString() method. Anchor action type in susi response contains link and text.        "link": "https://www.openstreetmap.org/#map=13/1.2896698812440377/103.85006683126556",        "text": "Link to Openstreetmap: Singapore" Here the text is the text we show in textview and link is used to show web content in the browser when user click on text. So first link and text are attached together like "<a href=\"" +susiResponse.getAnswers().get(0).getActions().get(i).getAnchorLink() + "\">" + susiResponse.getAnswers().get(0).getActions().get(1).getAnchorText() + "</a>" Here text between the tag is description of link and after parsing we show this text in textview. It can be parsed using fromHtml method of Html class and textview is made clickable by chatTextView.setMovementMethod(LinkMovementMethod.getInstance()); Resources Main site link of Html class: https://developer.android.com/reference/android/text/Html.html Blog link on how to show an Html string in an Android textview: http://alvinalexander.com/android/how-show-html-string-in-android-textview-webview

Continue ReadingHow to Parse HTML Tags and Anchor Clickable Links in SUSI Android App

Implementing Advanced Search Feature In Susper

Susper has been provided ‘Advanced Search’ feature which provides the user a great experience to search for desired results. Advanced search has been implemented in such a way it shows top authors, top providers, and distribution regarding protocols. Users can choose any of these options to get best results. We receive data of each facet name from Yacy using yacy search endpoint. More about yacy search endpoint can be found here:  http://yacy.searchlab.eu/solr/select?query=india&fl=last_modified&start=0&rows=15&facet=true&facet.mincount=1&facet.field=host_s&facet.field=url_protocol_s&facet.field=author_sxt&facet.field=collection_sxt&wt=yjson For implementing this feature, we created Actions and Reducers using concepts of Redux. The implemented actions can be found here: https://github.com/fossasia/susper.com/blob/master/src/app/actions/search.ts Actions have been implemented because these actually represent some kind of event. For e.g. like the beginning of an API call here. We also have created an interface for search action which can be found here under reducers as filename index.ts: https://github.com/fossasia/susper.com/blob/master/src/app/reducers/index.ts Reducers are a pure type of function that takes the previous state and an action and returns the next state. We have used Redux to implement actions and reducers for the advanced search. For advanced search, the reducer file can be found here: https://github.com/fossasia/susper.com/blob/master/src/app/reducers/search.ts The main logic has been implemented under advancedsearch.component.ts: export class AdvancedsearchComponent implements OnInit {   querylook = {}; // array of urls   navigation$: Observable<any>;   selectedelements: Array<any> = []; // selected urls by user changeurl(modifier, element) { // based on query urls are fetched // if an url is selected by user, it is decoded   this.querylook['query'] = this.querylook['query'] + '+' + decodeURIComponent(modifier);   this.selectedelements.push(element); // according to selected urls // results are loaded from yacy   this.route.navigate(['/search'], {queryParams: this.querylook}); } // same method is implemented for removing an url removeurl(modifier) {   this.querylook['query'] = this.querylook['query'].replace('+' + decodeURIComponent(modifier), '');   this.route.navigate(['/search'], {queryParams: this.querylook}); }   The changeurl() function replaces the query with a query and selected URL and searches for the results only from the URL provider. The removeurl() function removes URL from the query and works as a normal search, searching for the results from all providers. The source code for the implementation of advanced search feature can be found here: https://github.com/fossasia/susper.com/tree/master/src/app/advancedsearch Resources Using Postman for analyzing an endpoint of an API: https://www.getpostman.com/docs Redux documentation: http://redux.js.org/docs/introduction/

Continue ReadingImplementing Advanced Search Feature In Susper

Writing Browser Specific CSS for Susper in Angular

In Susper, we were facing a unique problem for Information box and Analytics box alignment. At a width of around 1290 px, the boxes fit perfectly in Firefox as shown: However, they were slipping to the next line in Google Chrome browsers for the same dimension(1290 px) The solution to this issue was to write browser-specific CSS. The two most commonly used browser specific tags are @-moz-document url-prefix() { }: This tag is used to target the Mozilla Firefox browser in particular. Anything written within the curly braces will not apply to any other browser. @media screen and (-webkit-min-device-pixel-ratio:0) { }: This tag is used to target all browsers that support webkit such as Chrome, Safari etc. For our problem, we need to use @media screen and (-webkit-min-device-pixel-ratio:0) { } This was how the code was written for both the components (Information box and Analytics box). Please refer to infobox.component.css and statsbox.component.css for the entire code. @media screen and (-webkit-min-device-pixel-ratio:0) { @media screen and (max-width: 1300px) { .card { width: 366px; } } } @media screen and (max-width: 1280px) { .card { width: 366px; } As a result of this snippet of code, we see the following effects: In Chrome, the boxes change to a smaller width at 1300px itself, thus preventing it from slipping to the next line In Firefox, the boxes change to a smaller width only at 1280px, and not at 1300px, thus achieving the exact design we envisioned. This is how the display finally looks in Chrome: References: Stack overflow on specific CSS tags for Chrome: https://stackoverflow.com/questions/9328832/how-to-apply-specific-css-rules-to-chrome-only Stack overflow on specific CSS tags for Firefox: https://stackoverflow.com/questions/952861/targeting-only-firefox-with-css

Continue ReadingWriting Browser Specific CSS for Susper in Angular

Making a Sticky Top Navigation bar for Susper using Angular

A lot of websites, require a top navigation bar that sticks to the top, irrespective of the screen dimension size. This blog deals with how the top navigation bar was made sticky in Susper. Using the correct Bootstrap classes. Notice the code enveloping the navigation bar. <nav class="top-nav navbar navbar-static-top navbar-default"> class="container-fluid"> class="navbar-header" id="navcontainer"> ... </div> </div> </nav> Points to note: Using navbar and navbar-default creates a standard gray navigation bar. Using navbar-static-top makes the navbar stick only to the top of the page and disappear on scrolling down. Using container-fluid creates a container for the contents of the navbar with wide margins  Now we also need to write some personalized CSS code. Notice the classes navcontainer and  top-nav. This is the CSS code for these classes: .top-nav{ margin-bottom: 0; } #navcontainer { height: 65px; width: 100vw; }#navcontainer ul { margin: 0; padding: 0; list-style-type: none; } Points to note: Margin and padding can be set according to how the navbar should look. Click here to know the difference between margins and padding. The height has been customized to 65px in Susper, with a width of 100vw(entire viewpost width). Lastly, if your navigation bar is inside the body tag, remember that by default, body has a top margin of 57 px. As a result you may see an extra white space on top of your navigation bar. To remove this: Move the navigation bar code out of the body tag. If you can’t then, Place your navigation bar in a container ( resultContainer on the Susper result page) and write this in your CSS file. .resultContainer{ margin-top: -57px; } References: For a tutorial on the various Bootstrap classes on navigation bars refer to: https://www.w3schools.com/bootstrap/bootstrap_navbar.asp For a tutorial on the making sticky navigation bars refer to: https://stackoverflow.com/questions/14667829/how-to-create-a-sticky-navigation-bar-that-becomes-fixed-to-the-top-after-scroll

Continue ReadingMaking a Sticky Top Navigation bar for Susper using Angular