NGINX + UWSGI + Ubuntu 16

If you use Python as part of your stack in developing a web architecture you most likely will also be utilizing NGINX with UWSGI together. In the following article I will give step by step instructions on how to set this up assuming you are using sockets as part of your installation.

What is UWSGI?: A full stack for building application servers for python and various other languages.

What is NGINX?: Is simply a web server or proxy server for various protocols but can also do load balancing, etc.

NGINX Installation:

  1. sudo apt-get install nginx
  2.  
  3. #The next command checks that nginx is running
  4. ps -auxw | grep nginx
  5.  
  6. #Stop the service
  7. sudo service nginx stop
  8. #Start the service
  9. sudo service nginx start
  10. #Restart the service
  11. sudo service nginx restart
  12. #nginx status
  13. sudo service nginx status

Once it is installed you will have two folders. One called “sites-enabled” and one called “sites-available” located under /etc/nginx/ folder. The “sites-enabled” folder usually holds a symbolic link to the actual file sometimes in “sites-available” folder. But really can be hosted anywhere on the machine.

If you want to remove the default site link you can

  1. sudo rm /etc/nginx/sites-enabled/default

Create our symbolic link in sites-enabled if we already have a nginx.conf file ready.

  1. cd /etc/nginx/sites-enabled/
  2.  
  3. sudo ln -s ~/path/to/nginx/conf/nginx.conf nginx.conf

It’s important to note that you may run into permissions errors depending on where you are running your nginx.conf from. So you may need to grant permissions to your users home folder.

  1. sudo chmod 755 /home/user_your_run_your_app_from/

UWSGI Installation:

  1. sudo apt-get install uwsgi
  2.  
  3. #Stop uwsgi
  4. sudo service uwsgi stop
  5. #Start uwsgi
  6. sudo service uwsgi start
  7. #Restart uwsgi
  8. sudo service uwsgi restart
  9. #uwsgi status
  10. sudo service uwsgi status

It you want to log to a custom folder your uwsgi.ini file will need “logto” folder specified and the correct permissions set.

  1. cd /var/log/
  2. sudo touch /var/log/mylogfile
  3. sudo chmod 666 /var/log/mylogfile

You uwsgi.ini will also have a pid file “pidfile” which we will need to create.

  1. cd /tmp/
  2. touch mypid.pid
  3.  
  4. #Ownership
  5. sudo chown THEUSER:www-data mypid.pid
  6.  
  7. #Ensure permissions are set to -rw-r--r--

UWSGI INI Options:

You can add whatever options you prefer as part of your uwsgi.ini setup file. However the below are usually the ones you should use.

  1. [uwsgi]
  2. socket = :5006 #or whatever socket you want
  3. chmod-socket = 777
  4. pidfile=/tmp/mypid.pid
  5. master = True
  6. chdir = /home/user_your_run_your_app_from/
  7.  
  8. #NOTE that you may or may not need this depending on how you setup your server.
  9. plugin = python3
  10. wsgi_file = my_python_class.py #File that runs your application
  11. module = whatever
  12. callable = app #Set default WSGI callable name.
  13. logto = /var/log/mylogfile

These are not all the options you can pick. You can also set “stats”, “buffer-size”, “processes”, “threads”, “env”, etc. Just review the docs and pick what is right for you.

The option “env” you can add many times and for each time you use it you can set environment variable for use with your application.

NOTE: If you are running flask and use send_file and run as Python3.5 you may get the error “SystemError: <built-in function uwsgi_sendfile> returned a result with an error set”. If you do then you must add the following to your uwsgi.ini file “wsgi-disable-file-wrapper = true”.

NGINX Conf Example:

  1. upstream flask {
  2. server 0.0.0.0:5006;
  3. }
  4. server {
  5. client_max_body_size 1G;
  6. listen 0.0.0.0:8081;
  7. server_name localhost;
  8. access_log /var/log/nginx/access_log combined;
  9. location / {
  10. include uwsgi_params;
  11. uwsgi_pass flask;
  12. }
  13. location /static/ {
  14. alias /path/to/my/app/static/;
  15. }
  16. }
  17. fastcgi_intercept_errors on;

UWSGI Service:

Sometimes we want to run our app as a service if you do then below are the steps for that.

  1. sudo nano /etc/systemd/system/myawesomeservice.service
  2.  
  3. #Enter the contents below
  4.  
  5. [Unit]
  6. Description=uWSGI instance to serve My Service
  7. After=network.target
  8. [Service]
  9. User=user_to_run_as
  10. Group=www-data
  11. WorkingDirectory=/path/to/my/app/
  12. ExecStart=/usr/bin/env bash -c 'uwsgi --ini /path/to/my/app/uwsgi.ini --uid user_to_run_as --gid www-data'
  13. Restart=always
  14. RestartSec=3
  15. [Install]
  16. WantedBy=multi-user.target

Next you need to ensure the user has access to your folder with correct ownerships.

  1. chown -R user_to_run_as:www-data /path/to/my/app/*

Next enable the service and start the service.

  1. sudo systemctl enable myawesomeservice
  2. sudo systemctl daemon-reload
  3. sudo systemctl start myawesomeservice
  4. sudo service myawesomeservice status

JavaScript: Download Object Data

Sometimes you just want to send an object to be downloaded in which you don’t have any special requirements just download the data. To do so is really straight forward. Create the blob with the type then create the blobs object url.

  1. var blob = new Blob([JSON.stringify(myDataObject)], {type: "application/force-download"});
  2. url = URL.createObjectURL(blob);
  3.  
  4. <a target="_blank" href={url} download="file.extension" />

1974 Mustang II Center Console Redone

In July 2017 I had the center console redone. I got some of the replacement parts from Phil Schmidt at Mustang II Speciality Shop New Used Obsolete 1974-1978 Ford Mustang II Parts (ash tray, coin tray, emergency brake rubber, automatic ​selector surround, center council brackets and the placement diagram). I also had Brett McHugh refinish the center console so it would be back to it’s original state and it did!

Before Pics:

After Pics:

Python: Working with DateTimes

In this tutorial I will show you the different ways of working with dates and times in python. Which includes working with milliseconds. You should note this isn’t all available options just some that I have encountered over the years.

Install Python Packages:

Open cmd/terminal and if required navigate to your sites working folder. (note: if you are working in a virtual env you should ensure you source it first).

  1. pip install python-dateutil

There are many different packages that we can use to work with date and times. You need to decide what is right for you.

dateutil:

The following will convert the date string you give it fast and easily. This gives you back the datetime object. Notice how we don’t need to pass it a date time format. To me this is very convenient.

  1. from dateutil import parser
  2.  
  3. date_str = '2017-06-06'
  4. date_time_str = '2017-06-07 12:34'
  5. date_time_str_2 = '2017-06-07 12:34:46'
  6. date_time_str_3 = '2017-06-07 12:34:42.234'
  7.  
  8. result = parser.parse(date_str)
  9. print(result) #2017-06-06 00:00:00
  10. result = parser.parse(date_time_str)
  11. print(result) #2017-06-07 12:34:00
  12. result = parser.parse(date_time_str_2)
  13. print(result) #2017-06-07 12:34:46
  14. result = parser.parse(date_time_str_3)
  15. print(result) #2017-06-07 12:34:42.234000

datetime:

The following will convert the date string you give it fast and easily. This gives you back the datetime object. Notice how we need to pass the format of the datetime. If you don’t you will get an exception. This is a convenient way if you know the format before hand. But that might not always be the case.

  1. import datetime
  2.  
  3. date_str = '2017-06-06'
  4. date_time_str = '2017-06-07 12:34'
  5. date_time_str_2 = '2017-06-07 12:34:46'
  6. date_time_str_3 = '2017-06-07 12:34:42.234'
  7.  
  8. result = datetime.datetime.strptime(date_str, "%Y-%m-%d")
  9. print(result) #2017-06-06 00:00:00
  10. result = datetime.datetime.strptime(date_time_str, "%Y-%m-%d %H:%M")
  11. print(result) #2017-06-07 12:34:00
  12. result = datetime.datetime.strptime(date_time_str_2, "%Y-%m-%d %H:%M:%S")
  13. print(result) #2017-06-07 12:34:46
  14. result = datetime.datetime.strptime(date_time_str_3, "%Y-%m-%d %H:%M:%S.%f")
  15. print(result) #2017-06-07 12:34:42.234000

The above all works however the following example will not. Why do you think this is?

  1. import datetime
  2.  
  3. date_time_str = '2017-06-07 12:34:46'
  4.  
  5. try:
  6. datetime.datetime.strptime(date_time_str, "%Y-%m-%d %H:%M:%S")
  7. except:
  8. pass #just for this example don't do this lol

The reason is because datetime expects the correct format to be supplied. We gave it hour minute second but not milliseconds. You will get the following exception (ValueError: unconverted data remains: .234)

Timestamps:

Sometimes we want to convert the date to unix (epoch) time or vise versa.

From Date:
  1. from dateutil import parser
  2. from datetime import timezone
  3.  
  4. date_time_str = '2017-06-07 17:34:42.234'
  5. result = parser.parse(date_time_str)
  6.  
  7. timestamp = result.replace(tzinfo=timezone.utc).timestamp()
  8. print(timestamp) #1496856882.234

This gives us the timestamp as a float as 1496856882.234.

From Timestamp:
  1. from dateutil import parser
  2. import datetime
  3.  
  4. timestamp = 1496856882.234
  5.  
  6. result = datetime.datetime.fromtimestamp(timestamp)
  7. print(result) #2017-06-07 13:34:42.234000
  8.  
  9. result = datetime.datetime.utcfromtimestamp(timestamp)
  10. print(result) #2017-06-07 17:34:42.234000

Get Date Parts:

If you want to get specific date parts such as the year, month, day, hour, etc.

  1. import datetime
  2. from dateutil import parser
  3.  
  4. result = parser.parse(date_time_str_3)
  5. print(result) #2017-06-07 12:34:42.234000
  6.  
  7. year = result.year #2017
  8. month = result.month #6
  9. day = result.day #7
  10. hour = result.hour #12
  11. minute = result.minute #34
  12. second = result.second #42
  13. millisecond = result.microsecond #234000

Add To Date:

If you want to add time to a date.

  1. import datetime
  2. from dateutil import parser
  3. from datetime import timezone, timedelta
  4.  
  5. date_time_str = '2017-06-07 17:34:42.234'
  6. result = parser.parse(date_time_str)
  7. print(result) #2017-06-07 17:34:42.234000
  8.  
  9. timestamp = result.replace(tzinfo=timezone.utc).timestamp()
  10. print(timestamp) #1496856882.234
  11.  
  12. #Add 10 seconds to datetime
  13. new_time = int((datetime.datetime.fromtimestamp(timestamp) + timedelta(milliseconds=10000)).timestamp() * 1000)
  14. print(new_time) #1496856892234

As you can see you can 10 seconds has been added the datetime.

datetime strftime

  1. from datetime import datetime
  2.  
  3. now = datetime.now()
  4. datetime_str = now.strftime("%Y-%m-%d %H:%M:%S")
  5. print(datetime_str)

datetime fromisoformat

  1. from datetime import datetime
  2.  
  3. print(datetime.fromisoformat("2024-04-09 13:48:20"))

 

1974 Mustang II Timing Cover Fix

In June 2017 while getting my tach adapter put on I noticed that I was leaking coolant yet again. I had just had the water pump replaced but this time it was coming from the timing cover itself. In order to take off the timing cover I had to take off all the puly’s, the water pump the radiator, the front grill, the bumper. It was a lot of work that as I am writing this is still ongoing. I also found that a head light was cracked so I am also waiting on that. Determining the right gasket set was a little difficult as my engine seems to be made up of a lot of different year parts. For example my water pump came from a 70’s (D00E-D). The timing cover is RP-E5AE-6058-FA, Top timing cover sprocket E3AE-A3.

Before Pics:

Once I got it all back together the pressure was good had a tiny leak on the water pump but after I snugged it a little more and added a missing bolt I didn’t notice till after all worked well.

After Pics:

 

 

 

 

Javascript: Math Functions

In this post I will show you how to perform math operations such as min, max, etc.

Min:

  1. //Array
  2. var maxValue = Math.min.apply(Math, myArray);
  3.  
  4. //Object Array
  5. Math.min.apply(Math,myObjectArray.map(function(v){return v,key;}));

Max:

  1. //Array
  2. var maxValue = Math.max.apply(Math, myArray);
  3.  
  4. //Object Array
  5. Math.max.apply(Math,myObjectArray.map(function(v){return v,key;}));

Sum:

  1. var sumValue = myArray.reduce(function(a, b) { return a + b; }, 0);

Average:

  1. var avgValue = sumValue / myArray.length;

Standard Deviation:

  1. var stdevValue = Math.sqrt(sumValue);

Python: CSV from Array

In this tutorial I will explain how to turn an array to a csv file. I will show you two ways. One is in memory and the other is to a file.

For both ways you need to import csv and io package.

  1. import csv, io
Way 1 Write (In Memory):
  1. #Create the string buffer
  2. output = io.StringIO()
  3.  
  4. #Setup the csv writer to write the results to a string buffer
  5. wr = csv.writer(output, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
Way 2 Write (File):
  1. #Crate the file itself in write mode
  2. f = open('filename.csv', 'w')
  3.  
  4. #Setup the csv writer to write the results to a file.
  5. wr = csv.writer(f, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)

Technically both ways have the same setup for the csv writer. Then to write results to the csv writer you then pass an array of values like below.

  1. wr.writerow(['123',5,4,'value'])

To Read the contents of the file or string buffer depends on which way you chose. I show you those ways below.

Way 1 Read (In Memory):
  1. b = bytes(output.getvalue(), 'utf-u')
Way 2 Read (File):
  1. f.close()
  2. file_data = open('filename.csv', 'r').read()

If you want to send the file down using something like flask send_file then you need to convert it to BytesIO.

  1. buffer = BytesIO()
  2. buffer.write(b)
  3. #You must seek to beginning otherwise it won't send anything back.
  4. buffer.seek(0)

Now if you are sending it as a file back to the user and are using something like flask this is how you do that. Pretty straight forward.

  1. return send_file(buffer, mimetype='application/octet-stream', as_attachment=True, attachment_filename='myFile.csv')

Python: Flask Resource

This tutorial helps setup flask restful api’s.

Install Python Packages:

Open cmd and navigate into your testApp folder and run the following commands.

  1. pip install flask-RESTful && pip freeze > requirements.txt
__init__.py:

On the init of your application you will need to setup flask_restful. There are config options you could set for config.py. Look into it!

  1. from flask_restful import Api
  2. api = Api(app)
  3.  
  4. #Add api endpoints
  5. #Get
  6. api.add_resource(home.views.MyResource, '/home/getMyData/')
  7.  
  8. #Post
  9. api.add_resource(home.views.MyResource, '/home/getMyData/', methods=['POST'])
Setup home views.py:

You need to import Resource in it’s most simplistic form. However if you want to deal with request parameters add in reqparse and inputs. Inputs give you access to boolean that way a boolean can be parsed into a python boolean easily.

  1. from flask_restful import Resource, reqparse, inputs

You can now use get, post, etc. I will give you three examples below.

Get:

  1. class MyResource(Resource):
  2. def get(self):
  3. return {}

Get /w Parameter:

  1. class MyResource(Resource):
  2. def get(self, var):
  3. return {}

Get /w Parameter & Request Parameter:

  1. class MyResource(Resource):
  2. def get(self, var):
  3. parser = reqparse.RequestParser()
  4. parser.add_argument('new_request_var', type=str, default='')
  5.  
  6. #If you want to have a boolean request parameter do the following.
  7. parser.add_argument('new_request_var_bool', type=inputs.boolean, default=False)
  8.  
  9. args = parser.parse_args(strict=True)
  10. new_request_var = args['new_request_var']
  11. new_request_var_bool = args['new_request_var_bool']
  12.  
  13. return {}

Post:

  1. class MyResource(Resource):
  2. def post(self):
  3. return {}

CSS: Bootstrap Panel

In this tutorial I will show you how how to use bootstrap with React/Node to make a panel using just bootstrap.

You will need bootstrap and jquery.

  1. npm install bootstrap@3.3.7 --save
  2. npm install jquery@3.2.1 --save
  3. npm install file-loader --save
  4. npm install url-loader --save

You will also need to update webpack.config.js and add the following under “loaders”.

  1. { test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: "file-loader" },
  2. { test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: "url-loader?limit=10000&mimetype=application/font-woff" },
  3. { test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: "url-loader?limit=10000&mimetype=application/font-woff" },
  4. { test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: "url-loader?limit=10000&mimetype=application/octet-stream" },
  5. { test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: "url-loader?limit=10000&mimetype=image/svg+xml" }

Next you need to require bootstrap and jQuery.

  1. window.jQuery = window.$ = require("jquery");
  2. require("bootstrap");

You will need to also require the bootstrap css.

  1. require("bootstrap/dist/css/bootstrap.min.css");

The following is all you then need to display a css like panel.

  1. <div className="panel panel-default">
  2. <div className="panel-heading">
  3. <h3 className="panel-title">Panel Title</h3>
  4. </div>
  5. <div className="panel-body">
  6. Panel Text
  7. </div>
  8. </div>

CSS: Bootstrap DropDown From Text Click

In this tutorial I will show you how how to use bootstrap with React/Node to make a dropdown from a text or image on click using just bootstrap.

You will need bootstrap and jquery.

  1. npm install bootstrap@3.3.7 --save
  2. npm install jquery@3.2.1 --save
  3. npm install file-loader --save
  4. npm install url-loader --save

You will also need to update webpack.config.js and add the following under “loaders”.

  1. { test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: "file-loader" },
  2. { test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: "url-loader?limit=10000&mimetype=application/font-woff" },
  3. { test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: "url-loader?limit=10000&mimetype=application/font-woff" },
  4. { test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: "url-loader?limit=10000&mimetype=application/octet-stream" },
  5. { test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: "url-loader?limit=10000&mimetype=image/svg+xml" }

Next you need to require bootstrap and jQuery.

  1. window.jQuery = window.$ = require("jquery");
  2. require("bootstrap");

You will need to also require the bootstrap css.

  1. require("bootstrap/dist/css/bootstrap.min.css");

The following is all you then need to display a css like dropdown.

  1. <div className="btn-group" >
  2. <a className="btn btn-primary dropdown-toggle" data-toggle="dropdown" href="#">testHeading</a>
  3. <ul className="dropdown-menu">
  4. <li>My Value</li>
  5. </ul>
  6. </div>

unRaid: Midnight Commander

If you are not familiar with unRaid go here. unRaid is a raid 4 implementation. If is basically a software raid.

If you want to work with the file system of unRaid whether it to be to move files or for another reason use midnight commander.

Start putty and connect to the unRaid server. Then once logged in run the following command for midnight commander.

  1. mc

 

Postgres: String to Rows

In this tutorial I will show you how to convert a string to rows delimited by a character.

There are three ways to do this all lead to the same answer.

  1. --METHOD 1
  2. SELECT split_data FROM regexp_split_to_table('I love programming!', E' ') AS split_data;
  3.  
  4. --METHOD 2
  5. SELECT split_data
  6. FROM unnest(string_to_array('I love programming!',' ')) AS split_data;
  7.  
  8. --METHOD 3
  9. SELECT split_string.arr[i] as split_data
  10. FROM (
  11. SELECT generate_series(1, array_upper(arr, 1)) AS i, arr
  12. FROM (SELECT ARRAY['I','love','programming!'] arr) t
  13. ) split_string;
Results:

Each executes in 11ms based on my analysis. Preferably I would go with “regexp_split_to_table”

1974 Mustang II Dash Wiring Repair & Update

In 2016 I also decided to redo a lot of the dash. The cluster cover was not correct and had a big hole in it, missing cluster plastic cover. No vents. Gauges in wrong areas. Bad wiring and interior lights not working.

I got a lot of parts from from Phil Schmidt at Mustang II Speciality Shop New Used Obsolete 1974-1978 Ford Mustang II Parts such as cluster plastic, cover, wire connectors, tach reducer and vents.

Here are the before pictures.

I decided to get rid of the custom tachometer that I had mounted to the steering column and go with the original tach. In order to do this I needed a tach reducer and a MSD tach adapter 8920. I don’t have the tach adapter yet but I did find a tach reducer and had that mounted in preparation. Also my dash cluster’s connector was broken so I had to get that replaced. I also had to replace the tach connector as that was not the correct one as well.

Here is it finally all done.

1974 Mustang II Engine Harness Replacement

In 2016 I decided to completely redo the engine harness. I was constantly getting  battery draining issues. Everytime I turned on my blinker the volts gauge would jump all over the place.

I will admit when I first started this project I thought ah this shouldn’t take too long to do. I was definitely wrong. It took almost a month of work. I had to find wiring diagrams and get them blown up so I could read them. I also got them laminated because I would be full of grease and they would be easily cleaned.

I had the car turned around in the garage so the front was facing out so I could work easier. Mike from Auto Electric MD taught and helped me redo all of the electrical in the engine bay and straight through to the dashboard. If you want to see the dashboard electrical restore I will be putting that up shortly.

Here are the before pictures.

After removing all the electrical tape from the wires I found that a good majority of the wire was corroded, connected improperly and completely overused. The voltage regulator was not setup properly. The 4th wire on the voltage regulator was why the car wasn’t charging.

Little by little we would trace wires in and compare to what the diagram said. I think labelling took the longest. Because you have to ensure everything is correctly mapped as to how it came off. We even had to disconnect the dashboard area to ensure wires went to where they actually said they went.

In trying to do replacements we had to disconnect the starter to replace and re-route cabling. That was a major part of the redo. We identified early on that a lot of what had been done could be routed better. The less cable the better. In disconnecting the starter it cracked the solenoid. Yes it actually cracked… The heat from the headers made the starter brittle. The plan was to put a heat cover on the starter next summer. So we had to replace the starter now. If you have ever worked on a 1974 Mustang II with a 302 engine. You will find that this is not done easily. We had to jack the engine in order to pull out the starter. What a nightmare to get that done but we did it. The alternator wasn’t setup correctly as well because the contact points were too close to the valve covers. So Mike had to rebuild the starter and alternator before we could continue. The voltage regulator seemed to not be correct as well so we put in a better one. All the head lights were redone. new connectors, etc.

Here are some pictures of the work as we were going through it.

By the time we were done we had probably 5 pounds of over used wire. No joke! At this time the tack still doesn’t work but it will summer 2017!

 

 

 

1974 Mustang II Exhaust Replacement

In summer 2016 I decided to get the exhaust replaced. I was looking pretty manky. It was never done right so it was time.

Here are the before pictures. Pretty shotty job if I do say so myself.

I brought it over to Carlane Auto Centre in Guelph, Ontario. They estimated and I got it done the right way. I believe it took them a day to get it all done. It looked amazing!

Here are the after pictures.

All in all very happy with the new exhaust system!

JavaScript: Node & Lodash

In this tutorial we will be giving out some quick examples of Lodash functionality. To know more go here. There are so many examples and ways of doing Lodash. Check out the documentation.

Q: What is Lodash you ask?
A: It is a toolkit of Javascript functions that provides clean, performant methods for manipulating objects and collections. It is a “fork” of the Underscore library and provides additional functionality as well as some serious performance improvements.

First thing we need to do is install it. You will need a node site already ready to go. If you don’t have one you can follow this tutorial on setting a basic one up.

  1. npm install lodash --save

On whatever page you are working with all you need to do is add the following to where your requires are.

  1. var _ = require('lodash');

Now we can use the functionality as we wish. I will do some basic uses below.

Array difference:

If we want to find the difference of an array to the second array. The result would be “1” because 1 is not in the second array. Notice how it does not compare the second array to the first. It’s only checking which values 2 or 1 don’t exist in the second array.

  1. _.difference([2, 1], [2, 3])
Array uniqWith:

If you want to get the unique items in an array you could use the following. It would return “2 45 3 7 8 1” only notice that the additional 45 is not displayed. It has been removed.

  1. _.uniqWith([2, 45, 3, 7, 8, 45, 1], __.isEqual)