Thursday, March 23, 2023

making a network directory writable for an internal webserver

In order to automate some of our processes, wanted a PHP page to trigger some file copy commands to a shared directory. Initially the network share was not writable by the web server.

I have made it writableby mounting it with file_mode=0777,dir_mode=0777

sudo mount -t cifs //Internal-IP/SHARENAME sharenamelocal -o uid=ourusername,username=username,password=xxxxx,file_mode=0777,dir_mode=0777

as suggested by ChatGPT :)


print jobs making notification sounds after Linux Mint upgrade to 21

After upgrading my machine from Linux Mint 20.3 to 21, print job notifications were making "ping" sounds. 

Opened the "Sound" configuration applet, Sounds tab - turned off the sound for "Showing notifications" after scrolling down. This seems to be a newly added sound, since earlier I had turned off all sounds. (At first glance, I didn't see it, but it was visible after scrolling down.)

Sunday, March 19, 2023

VNC error - startxfce4: X server already running on display :1

Saw this error in the logs when trying to start VNC on a machine where VNC was earlier working, but was now displaying a blank screen.

https://superuser.com/questions/1607082/tightvnc-in-ubuntu

advises to 

Change the ~/.vnc/xstartup file to:

#!/bin/sh
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
startxfce4 &

This worked - the session commands were not present before.

Saturday, March 18, 2023

Moodle Moove theme footer and support popup

The Moove theme on Moodle has a very prominent copyright notice, and the latest version of Moodle (4.1.1) seems to have added a "footer popup" button with a "?" sign, pointing to Moodle support - which might be confusing for our users. Hiding these using the Raw SCSS option in 
oursite.tld/admin/settings.php?section=themesettingmoove#theme_moove_advanced

footer::after {content: "© 2010 - 2023 (Our copyright notice). "} 

.card-body {
min-height: 100%;
}

footer#page-footer .row  {display:none;}

footer#page-footer .copyright  {display:none;}

footer#page-footer .footer-content-popover  {display:none;}

footer#page-footer   button{display:none;}

.stores {display:none;} 

a[href*='apple.com'] {display:none;}

nav.navbar .navbar-brand .logo img{

max-width:100%;

height:auto;

}

nav.navbar .navbar-brand .logo{

display:inline!important;}


setting up cloudflare origin certificates on Windows server

There seemed to be something wrong with win-acme which was preventing automatic renewals of the LetsEncrypt certificate on one of our servers. Possible reason could be that the server is switched off during the nights as it is a test / development server. Anyway, wanted to try Cloudflare's Origin server certificate instead, like we'd done for a Linux bitnami server earlier.

This post
https://bytefreaks.net/applications/cloudflare-origin-server-certificate-for-iis-10-server-on-windows-server-2016-to-allow-full-strict-mode-ssl-tls-encryption-mode
gives a good step-by-step guide. (Archived link)

Cloudflare's doc

points to

which is perhaps less clear.

When I try to select the cloudflare cert, the following error message is displayed:


I suppose that is because the certificate is from Cloudflare and not from one of the recognized certificate providers. Works with Cloudflare proxying in Full/ Strict mode. As the documentation says, "Site visitors may see untrusted certificate errors if you pause or disable Cloudflare on subdomains that use Origin CA certificates. These certificates only encrypt traffic between Cloudflare and your origin server, not traffic from client browsers to your origin."

Thursday, March 16, 2023

Cloudflare configurations for a new domain of ours

 
1. Made ourdomain.tld and www.ourdomain.tld proxied through cloudflare, from cloudflare dashboard - this is required for the next two points,

2. Added a page rule to redirect all requests to the bare domain ourdomain.tld to www.ourdomain.tld (which points to the google sites website).

3. Verified that "Always use HTTPS" is turned on in cloudflare Zone settings --> SSL/TLS --> Edge Certificates page. This will ensure that any http requests will be redirected to https.

Tuesday, March 14, 2023

printing a poster on multiple A4 sheets with a home office printer

Googling brought up PosteRazor - a nifty tool - which has an online webassembly-based version also. Worked perfectly in Firefox even on my underpowered machine with only 4 GB RAM running Linux Mint. Embedding their how-to video below - 



Saturday, March 11, 2023

pop-up Window - Modal box - in javascript

This is useful when you want to load a list of files or something like that when the user click on a link, using Ajax - How To Make a Modal Box With CSS and JavaScript

Friday, March 10, 2023

Listing / Getting Keys Values etc from javascript objects

https://www.educative.io/answers/how-to-get-keys-values-and-entries-in-javascript-object

If we don't know what a javascript object contains, we can use these to learn more about the object - 

    Object.keys(obj) – returns all the keys of object as array
    Object.values(obj) – returns all the values of the object as array
    Object.entries(obj) – returns an array of [key, value]

and we can check the length of the object using Object.keys(obj).length

using which we can loop through the object, or we can use foreach or for var in array and so on.

messages.forEach(function(message) {
   console.log(message);
}

for(let message of messages){
   console.log(message);
}

for(let key in messages){
       console.log(messages[key]);
 }

data.messages.forEach((obj, i) => {
     console.log("msgFrom", obj.msgFrom);
     console.log("msgBody", obj.msgBody);
 });

data.messages.map((obj, i) => {
     console.log("msgFrom", obj.msgFrom);
     console.log("msgBody", obj.msgBody);
 });

We can also implement lookup tables using objects,

https://www.educative.io/answers/how-to-use-objects-for-lookup-in-javascript




Thursday, March 09, 2023

one more link to remove files from github repo

To add to my earlier post about removing files from a github repository, and from git history, github's documentation is at

https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/removing-sensitive-data-from-a-repository#using-git-filter-repo

 

python3 -m pip install --user git-filter-repo

Requirement already satisfied: git-filter-repo in ./.local/lib/python3.8/site-packages (2.34.0)

 

Tuesday, March 07, 2023

HFS+ on Linux - or exFAT for Linux/Windows/Mac compatibility

HFS plus formatted Mac removable SSD (USB external hard disk) was not recognized on Windows and not writable on Linux. 

perhaps can be force mounted?
https://forums.linuxmint.com/viewtopic.php?t=283956

Or perhaps would need journaling turned off?
https://help.ubuntu.com/community/hfsplus

Anyway, with journaling turned off, exFAT is more compatible.

Trying to format it to exFAT on Linux -
https://unix.stackexchange.com/questions/61209/create-and-format-exfat-partition-from-linux

on my system,
sudo apt-get install exfat-utils exfat-fuse
(all installed already)
and then gparted

But greyed out in gparted - apparently a newer version of gparted is needed - my gparted version was 1.0 - so used Disks instead, OK.

But did not read on Windows. Reformatted in Windows, then OK. Some blocks issue, I guess. Or maybe needs to be GPT? I've not yet tried on Mac.

ffmpeg syntax to create movie from sequentially numbered frames

https://stackoverflow.com/questions/47670918/create-video-from-images-sorted-in-numerical-order-using-ffmpeg

ffmpeg -framerate $FRAMERATE -i "image%05d.jpg" output.mp4

and if we want XVID,

https://trac.ffmpeg.org/wiki/Encode/MPEG-4

ffmpeg -framerate $FRAMERATE -i "image%05d.jpg" -c:v mpeg4 -vtag xvid output.avi

(not yet tested for compatibility with our Blu Ray player.)

What I've used for 4K frames, 

ffmpeg -r 30 -f image2 -i "paranal_timelapse_vst_%05d.jpg" -vcodec libx264 -crf 15  -pix_fmt yuv420p paranal_timelapse_vst-4k-frames.mp4

Edit: 

# for starting at arbitrary frame number

ffmpeg -r 30 -start_number 405 -f image2 -i "paranal_timelapse_vst_%05d.jpg" -vcodec libx264 -crf 15  -pix_fmt yuv420p partial.mp4


Sunday, March 05, 2023

removing hyperlinks from PDF

Apparently in Adobe Acrobat, it is by Edit PDF -> Link -> Remove web links 

https://community.adobe.com/t5/digital-editions-discussions/how-to-remove-hyperlink-from-pdf/m-p/2236139

But then if the PDF is viewed inside Moodle, Moodle puts the web links back by parsing the text? Maybe. Works OK for a normal webserver.


Tuesday, February 28, 2023

lots of javascript recipes

Since I'm new to javascript and am trying to write some javascript code, lots of googling and some mis-steps. But useful, since loading json data and displaying can be done much, much faster with javascript rather than, for example, google apps script. Summarizing some "how-to"s below.
  • The addressing style using $ is usually used with jQuery. For example, 
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js">
    $(function () {
       // this runs on page load
       ourdropdownupdate();
       loadourdata();
    });


  • Javascript generally loads and executes from top to bottom linearly, except for async calls. So, within functions, for example, one would execute linearly. 

  • Global variables - variables declared outside function braces {} will be global variables. (But in google apps script, functions called from templated html will start execution from the top, as a separate thread, so the global variables initialized earlier would not be initialized for that execution.)

  • Returning Multiple Values from a Function - we need to either return an array or an object which has multiple properties.

  • Clear all rows in an html table except the first row
    var mytable = document.getElementById("mytableid");
    mytable.getElementsByTagName("tbody")[0].innerHTML = mytable.rows[0].innerHTML;
  • Create options in a dropdown list (select element in a form) -
    https://stackoverflow.com/questions/78932/how-do-i-programmatically-set-the-value-of-a-select-box-element-using-javascript
    https://www.w3schools.com/jsref/met_select_add.asp
    https://stackoverflow.com/questions/4618763/removing-all-option-of-dropdown-box-in-javascript
    var selecDay = document.getElementById("dateselector");
    selecDay.options.length = 0; // clear the select element of all options
    var option = document.createElement("option");
    var optiondate = new Date(optiondatemillisec);
    option.value = optiondate.toISOString().substring(0, 10);
    option.text = optiondate.toLocaleString('en-us', { day: 'numeric', month: 'short', year: 'numeric' })
    selecDay.add(option);


  • The function toISOString() (as used above) will generally return a string with zero offset - so, if we want the local time in the string, we need to get the offset and subtract it - not add it. 
    https://stackoverflow.com/questions/10830357/javascript-toisostring-ignores-timezone-offset
    javascript - Get month name from Date - Stack Overflow 
    var tzoffsetmilli = (new Date()).getTimezoneOffset() * 60000; //offset in milliseconds
    var localtimestring = (new Date(gmtDateTime.getTime() - tzoffsetmilli)).toISOString();


  • How to format numbers by prepending 0 to single-digit numbers? - Stack Overflow
    var formattedNumber = ("0" + myNumber).slice(-2);

  • How to display the abbreviated name of the user's timezone (not GMT offset) in JavaScript? - Stack Overflow
    The method of getTimezoneShort() mentioned in the page above does not display strings like IST for Indian Standard Time, instead it shows GMT +5:30 etc. One could use some jerry-rigged code taking the first letter as mentioned here, but that is not guaranteed to work across many timezones. I just displayed "local time" instead, and displayed the full timezone string elsewhere, as obtained with
    var timeZoneStr = Intl.DateTimeFormat().resolvedOptions().timeZone;

  • How do I check if file exists in jQuery or pure JavaScript? - the similar syntax as this one can be used as a get() replacement, like below, replacing HEAD with GET.
    $.ajax({
                        url: pathname1,
                        type: 'GET',
         success: function (data) {
          schdata = JSON.parse(data);
         },
           error: function () {
            // try something else                            
         }
    });
  • If we do a setInterval(), we may need to do a clearInterval() if we want to stop it - otherwise, if the element checked inside the setInterval is not found, there would be an exception and all javascript processing will stop. For example, this time counter -
     
    <div id='typingdiv'>
    <h3 id='seccounter'> </h3>
    <script>
    // Set the date we're counting down to
    var sec = 1;

    // Update the count down every 1 second
    var x = setInterval(function() {
    try {
    document.getElementById("seccounter").innerHTML = sec + "s ";
    sec++;
    }
    catch (err) {
    // error because the setInterval cannot find the element
    // after the ajax completes and overwrites the div.
    clearInterval(x);
    }
    }, 1000);
    </script>

  • A useful way to debug is to add console.log(err) in catch (err) blocks - or console.log some other string at someplace else - and to watch the console on the html page with a right-click, Inspect, and the Console tab.

  • If we have an iframe and want to pass parameters to it (or, in general, we want to pass parameters to javascript on a page), we need to use GET requests and not POST requests - https://stackoverflow.com/questions/831030/how-to-get-get-request-parameters-in-javascript - 
    var queryString = location.search
    let params = new URLSearchParams(queryString)
    // example of retrieving 'id' parameter
    let id = parseInt(params.get("id"))
    console.log(id)

Sunday, February 26, 2023

exporting to csv download from php

Following https://mehulgohil.com/blog/how-to-export-data-to-csv-using-php/

In our case, the required code was something like this:

header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename=filemastercsv_export.csv');
require("SessionCheck.php");
require("Constants.php");

$output = fopen( 'php://output', 'w' );
ob_end_clean();
$query= "select * from our_table_master f  where f.fileId > 50900 order by f.fileId";

$dbcon= mysql_connect($DBHOST,$DBUSER,$DBPASSWORD);

if(!$dbcon)
{
echo "Unable to connect to database !";
return;
}

mysql_select_db($DBNAME);

$totalrows= 0;

$result= mysql_query($query);

if (mysql_num_rows($result) == 0) {
    echo "No rows found, nothing to print so am exiting";
    exit;
}

while ($row = mysql_fetch_row($result)) {

    fputcsv($output, $row);

}

mysql_free_result($result);

mysql_close($dbcon);

exit;



Wednesday, February 22, 2023

twitter widget not loading in Edge browser

One of our pages which had an embedded twitter timeline widget had the issue that the twitter widget was not loading on Microsoft Edge browser, but would load on Firefox / Chromium. Looking at the console (right-click on the page, Inspect --> Console tab), found that Edge was reporting "Tracking prevention blocked a script resource from loading", along with a Bing search link.

 
So, we can add exceptions at
adding our domain name solved the issue.

Sunday, February 19, 2023

typewriter effect in js vs css

One disadvantage of using the typewriter effect in javascript instead of a spinner gif is that we can't replace the text with something else when finished -
$(#"divname").hide() does not seem to work.

What does seem to work is
document.getElementById('typingdiv').innerHTML = '<br><br>'

So we can use that with either a gif created with a "typing gif creator" like msgif.net or with a typewriter effect in css as the initial innerHTML.

Saturday, February 18, 2023

downloading from google cloud shell

Logging in to Google Cloud Shell, noted the "help" options, and a useful function -if we have some file on our home directory in google cloud shell and want to download it, we need not struggle with setting up ssh keys and so on - an easy way is to use

dl filename 

in cloud console - this means download to your client machine (browser). To download a file into cloud shell, we can use standard 

wget url

etc.

Friday, February 17, 2023

way to get "logger-like" continuously updating Google Apps Script Web app output

Since we need to use client-side Ajax to update html pages of the Google Apps Script Web app - https://developers.google.com/apps-script/guides/html/best-practices - one way to get a terminal-like or console log-like experience in the web app would be like this - 

 In the code.gs, 

function doGet() {
return HtmlService
.createTemplateFromFile('Page')
.evaluate()
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}

 In the Page.html, a <script> section at the beginning, which has our ajax code, and the <body> section containing the suitable formatted blocks with the relevant ids as called by the ajax functions. 

<script
src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.js">
</script>
<script>
$(function() {
google.script.run
.withSuccessHandler(displaylog1)
.
withFailureHandler(displaylog1err)
.getdata1();
});

function displaylog1err(data) {
var logger = document.getElementById('log');
logger.innerHTML += "<p>There was an error 1!</p>";
}


function displaylog1(data) {
var logger = document.getElementById('log');
logger.innerHTML += "<p>Done 1!</p>";
google.script.run.withSuccessHandler(displaylog2)
.getdata2();
// we have to call this inside displaylog1
// if we want getdata2() to execute
// only after getdata1() completes.
}

function displaylog2(data) {
var logger = document.getElementById('log');
logger.innerHTML += "<p>Done 2!</p>";
}
</script>
</head>
<body>
<pre id="log0">Logging the output here: </pre>
<pre id="log"> </pre>
</body>
</html>
 

The method mentioned at https://stackoverflow.com/questions/20256760/javascript-console-log-to-html for redirecting console.log did not work for me, maybe because console.log logs to the console of the client making the call, and in our case, the relevant functions are server-side. Or maybe there are other differences, like the (function () { }) call syntax seems to be different from the one below:

$(function() {
var old = console.log;
var logger = document.getElementById('log');
console.log = function () {
for (var i = 0; i < arguments.length; i++) {
if (typeof arguments[i] == 'object') {
logger.innerHTML += (JSON && JSON.stringify ? JSON.stringify(arguments[i], undefined, 2) : arguments[i]) + '<br />';
} else {
logger.innerHTML += arguments[i] + '<br />';
}
}
}

navigating the new Google Analytics 4 reports

Navigating the new analytics.google.com - takes a bit of getting used to.

  • A tour shows us the location of the main navigation elements.

  • Typing "tour" in the search box brings up some tours.

  • We can add the tracking code to subdomains also, no problems with tracking. So I deleted the separate property I had created for a subdomain from Admin --> Property Settings (of the relevant property) --> Move to Trash Can button (on the top right) as per
    https://support.google.com/analytics/answer/1042032?hl=en
     
  • We can access the tracking code from Admin --> Property Settings --> Data Streams --> Click on the relevant domain or stream --> View tag instructions (at the bottom of the page).

  • We can find stats for a particular page from Reports --> Engagement --> Pages and screens.

  • We're able to see stats of pages 
    (a) based on the page title and not based on the page domain - subdomain.ourdomain.org returns no results

    (b) based on the path excluding the domain name - /ourfoldername or index.php or index_new.php does show up.


    But for these, we have to change the drop-down accordingly, as in the screenshots below -
    For search based on page title - 




    For search based on page path -







    And it also seems to be a bit buggy - Searching for program gives only 2 results, while searching for  /program gives many results, and searching for program/ gives the 4 results we want.




Wednesday, February 15, 2023

javascript date comparison

Some interesting things about client-side javascript date comparison - 

  • if you want to compare with ==, you would need to prefix the variable with a + but > and < would work fine.

  • you can't just write
    if ( Date(datestring) < Date("2022-12-08 23:59:59" ) ) - you need to create new Date(string) explicitly, like
    var d1 = new Date("2022-12-07 23:59:59" );
    var d2 = new Date("2022-12-08 23:59:59" );
    var testdate = new Date(testdatestr);
    if ( testdate > d1 && testdate < d2 )

Tuesday, February 14, 2023

link dump of possible google apps and javascript solutions

(The following is a link dump of various possibilities searched out for implementing this, for which plain javascript without Google Apps script was finally used.)

Link dump:

We can automatically choose the user's timezone, and also have a timezone setting from any of these,

according to
 
 
 
"Correct way" - tested working

console.log(Intl.DateTimeFormat().resolvedOptions().timeZone)

Documentation
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/resolvedOptions
 
"Do we have some stats on the number of daily user visits to the schedule page? If so, that would be useful in planning the type of page - could even make it a static page with data delivered via ajax using client-side javascript. There are some limits to the number of Google Apps Script executions per day. For example, I'm using a URL Fetch function, that is limited to 100,000 calls per day. " 

Checking out client side javascript rendering, found that is faster than Google Apps Script, faster than our php/mysql also :)

Client-side example:
for which the code is at 
(takes the data from a text file saved in the same server)

Google Apps script example:
(takes the data from a Google Sheet)
Google Apps test code has now been put up at 
comments in the code show the various issues with the different approaches.
 
 

Monday, February 13, 2023

idea for updating website once a day

This post talks about manually downloading and uploading, using Google Apps Script only to generate the webpage once and save it to Drive.
https://stackoverflow.com/questions/70071534/i-want-to-use-google-app-script-with-html-service-to-create-a-static-html-file

I suppose the file saved to Drive can be uploaded to some other site using some sort of scripting running either on Google Apps Script or on our local server. 

Edit: Possible uploading to S3 bucket using Google Apps Script code similar to this

Edit Mar 2023: Implemented a Github Pages upload version using Github's REST API in the PHP translation of our Google Apps Script mentioned in a later post

https://github.com/hn-88/mktxt/blob/main/maketxt.php#L271

Sunday, February 12, 2023

Google photos - no bulk rotate option

I had several albums of photos imported into a google photos account, which needed to be rotated left. There seems to be no bulk rotate tool. So I wrote a SikuliX script - though it takes half a second per photo, at least it'll do the job unattended:

maxi=30 #the number of images in the album
i=0
sleeptime1=0.2
sleeptime2=0.2
sleep(1.0)
while(i<maxi):  
  type("R")            #rotate
  sleep(sleeptime1)
  type(Key.RIGHT)        #go to next   
  sleep(sleeptime2)
  i=i+1

Friday, February 10, 2023

Wednesday, February 08, 2023

for Income Tax Return (ITR) filing - reference

 https://www.etmoney.com/learn/income-tax/how-to-file-income-tax-return-itr-for-mutual-funds-on-new-tax-portal/

- a good reference for what details are needed, and where what needs to be filled up, for those with Capital gains from Mutual Funds. 

Edit: June 17 -

Apparently it's not a good idea to file before June 15 - https://www.livemint.com/money/personal-finance/why-you-should-avoid-filing-tax-returns-before-15-june-11686674063937.html

And while there is the option of uploading a csv file, in my case, the manual entry of the data needed for each Mutual Fund transaction was the easier way, since some of the data was not directly 1:1 matching in the format the IT department wanted vs the format provided by Kfintech / CAMS in their profit/loss statement. For example, the ASIN information was part of the "fund name" field, we need to copy-paste it out (or write code to check for it and take it out, which would make the process more complex). 

 And also found that Groww (and other such online mutual fund facilitators) provides only profit/loss from the funds purchased through Groww - Kfintech + CAMS statements are needed for the full picture. 

Also, for ease of opening the protected Excel sheets with Google Sheets, the method I used was to download them, open them locally using LibreOffice, then save as .ods or something like that without password protection, and then upload to Google sheets.

Tuesday, February 07, 2023

correct way to append with jquery

jquery append() actually appends an element. So, we need to make the append statement an entire row, in case we're appending to a table.

Populate html table on jQuery success event - Stack Overflow

table.append("<tr><td>"+elem.username+"</td><td>"+elem.name+"</td>   <td>"+elem.lastname+"</td></tr>");

and not something like

table.append("<tr><td>");
table.append(elem.username);
table.append("</td><td>");
etc.   

correct way to use jQuery append

Doing a table fill with AJAX, I was trying to use $('#maintable').append() multiple times in a loop, assuming that the append function is just appending whatever text I supply to it - but it turns out jQuery append actually appends a child element to the parent element. The above syntax is for jQuery and not plain javascript. 

I was ending up with content like

<tr></tr>
<tr></tr>
<tr></tr>
<td>Our data</td>
... and so on.

 So, I must append an entire row with a single append statement. In javascript, the function call can span multiple lines, no problem. Most probably the correct way would be to use maintable.tbody.append, though the snippet below works.  

function showThings(things) {

var maintable = $('#maintable');
maintable.empty();

for (var i = 0; i < things.length; i++) {
maintable.append('<tr><td>Row '+ i + '</td><td>' + things[i][0] + '</td><td>' +
things[i][1] + '</td><td>' +
things[i][2] + '</td><td>' +
things[i][3] + '</td><td>' +
things[i][4] + '</td><td>' +
things[i].length + '</td></tr>');

}

}

Monday, February 06, 2023

updating URL in descriptions

Adapted from an email from PB:

Executed the following SQL in the local postgresql database:

UPDATE file_information SET description = REPLACE(description,'http://old.domain/', 'https://new.domain/sss/') where description like '%old.domain%'

1539 row(s) affected.


UPDATE file_information SET description = REPLACE(description,'http://www.old.domain/', 'https://new.domain/sss/') where description like '%old.domain%'
354 row(s) affected.


Executed the following SQL on the web server:

UPDATE `our_file_master` SET `description` = REPLACE(description,'http://old.domain/', 'https://new.domain/sss/') where description like '%old.domain%'
(1022 rows)
and

UPDATE `our_file_master` SET `description` = REPLACE(description,'http://www.old.domain/', 'https://new.domain/sss/') where description like '%old.domain%'
(345 rows)

Sunday, February 05, 2023

exporting mysql table to csv

I wanted to export quite a large subset of a table which was using up 1.8 GB - first tried this method,
https://phoenixnap.com/kb/mysql-export-table-to-csv

 SELECT ourfield1, ourfield2, ourfield3 FROM ourTable
INTO OUTFILE '/path/to/ExportFile.csv'
FIELDS ENCLOSED BY '"'
TERMINATED BY ';'
ESCAPED BY '"'
LINES TERMINATED BY '\r\n'
;

This failed, --secure-file-priv is set to null(?) on our server and I didn't want to change that.

database - How should I resolve --secure-file-priv in MySQL? - Stack Overflow

Next plan was to export by redirecting a here document in a bash script. One hiccup was that the here doc should end with the end characters without any indent, and there should be no other characters after it on its line, otherwise the script fails to find the characters.

https://stackoverflow.com/questions/18660798/here-document-gives-unexpected-end-of-file-error 

Next issue was that the exported data was large, and probably not indexed, so
mysql: Out of memory (Needed 698144116 bytes)

For this, I would probably need to limit the number of result rows (by timestamp, in this case),

How do I get the current Unix time in milliseconds in Bash? - Server Fault



How to increment a variable in bash? - Ask Ubuntu

Then, the easiest way to code seemed to be to put the entire statement in one line, avoiding issues with SQL/bash multi-line statements:

$MYSQL --batch -u $MyUSER -h $MyHOST -p$MyPASS $DBS -e "select l.id, l.timecreated, l.userid, l.ip, f.filename from prefix_logstore_standard_log l left join prefix_files f on l.contextid = f.contextid where l.timecreated > $StartDate  and l.timecreated < $EndDate and l.action = 'viewed' and l.target='course_module' and f.filename like '%.pp%' order by l.timecreated;" > $FILE

This would actually give a tab separated file and not comma separated, but this is also imported without issues by Google Sheets.

 

easier way to handle RDP failure on Azure burstable VMs

Since leaving the Remote Desktop Protocol (RDP) port 3389 open is not recommended, I had tried to move one of the teams working on a couple of remote Windows servers on Azure B-series VMs to use ssh tunnels instead of directly RDP into the server. They could not get the tunnels to work with the reg file I sent them - probably because the paths were different. A workaround would be to just stop the remote desktop service when not in use, and start the service only when they need to use it. So, created a stoprdp.bat file, containing
net stop UmRdpService
net stop termservice

A similar restartrdp.bat file has
net stop UmRdpService
net stop termservice
net start termservice
net start UmRdpService

This is another easier way of doing the same thing described in my earlier post at https://hnsws.blogspot.com/2021/05/restarting-remote-desktop-via-ssh-or.html

port 2000 shown as open by netcat

Checking for open ports on one of our servers using netcat,
nc -zv our.server.name 2000
Connection to our.server.name 2000 port [tcp/cisco-sccp] succeeded!

Googling, found that this could be due to our ISP trying to block VOIP ports.